[flang-commits] [flang] ad0072d - Reland [flang][HLFIR] make copyin/copyout allocation inline (#224063) (#224570)

via flang-commits flang-commits at lists.llvm.org
Fri Sep 18 02:22:02 PDT 2026


Author: jeanPerier
Date: 2026-09-18T09:21:54Z
New Revision: ad0072d503a035d3e30fab668695a72d5774556f

URL: https://github.com/llvm/llvm-project/commit/ad0072d503a035d3e30fab668695a72d5774556f
DIFF: https://github.com/llvm/llvm-project/commit/ad0072d503a035d3e30fab668695a72d5774556f.diff

LOG: Reland [flang][HLFIR] make copyin/copyout allocation inline (#224063) (#224570)

This patch moves most allocations of copyin/copyout to the compiler so
that small buffers can be stack allocated, even when the loops are not
inlined. The main motivation is to allow better memory management when
the code ends-up in device region, but this also benefits CPU by giving
more control over allocations.

In the future, we may just consider inlining everything including
copyout, but very early attempts to do so showed that this increased WRF
compile times significantly at O2 for no benefits and the copyout runtime
was just modified to do "copy on change" so I do not want to inline it
until more this new approached is tested more broadly.

Hence this patch just moves to use `ShallowCopyDirect` for the copy-in
and a new `CopyOutAssignDirect` for the copy-out (so that the new copy
on change policy can be integrated) for non assumed-rank non polymorphic
copy-in/copy-out, and does the allocation inline for the buffer
following the same rules as in `inlineHLFIRCopyin` (i.e. small buffers
on stack, everything else on the heap for now).

A new "mustFree" boolean has to be added as a result to hlfir.copyin and
input from hlfir.copyout to tell if the buffer needs to be deallocated
or not (stack or heap allocated) to avoid having to make assumptions on
how the buffer was allocated in hlfir.copyout codegen.

Assited-by: AI

Relands #224063 with a CMakeLists.txt change that fixes shared library builds.

Added: 
    

Modified: 
    flang-rt/include/flang-rt/runtime/tools.h
    flang-rt/lib/runtime/assign.cpp
    flang-rt/lib/runtime/tools.cpp
    flang/include/flang/Optimizer/Builder/Runtime/Assign.h
    flang/include/flang/Optimizer/HLFIR/HLFIROps.td
    flang/include/flang/Optimizer/HLFIR/Passes.td
    flang/include/flang/Optimizer/Support/AllocationPolicy.h
    flang/include/flang/Runtime/assign.h
    flang/lib/Lower/ConvertCall.cpp
    flang/lib/Optimizer/Builder/Runtime/Assign.cpp
    flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
    flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
    flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRCopy.cpp
    flang/lib/Optimizer/Support/AllocationPolicy.cpp
    flang/lib/Optimizer/Support/CMakeLists.txt
    flang/test/Fir/allocation-policy-pipeline.fir
    flang/test/HLFIR/assumed-type-actual-args.f90
    flang/test/HLFIR/assumed_shape_with_value_keyword.f90
    flang/test/HLFIR/copy-in-out-codegen.fir
    flang/test/HLFIR/copy-in-out.fir
    flang/test/HLFIR/inline-hlfir-copy-stack.fir
    flang/test/HLFIR/inline-hlfir-copy.fir
    flang/test/HLFIR/memory-effects.fir
    flang/test/Lower/HLFIR/assumed-rank-calls.f90
    flang/test/Lower/HLFIR/call-postponed-associate.f90
    flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90
    flang/test/Lower/HLFIR/calls-assumed-shape.f90
    flang/test/Lower/HLFIR/calls-constant-expr-arg.f90
    flang/test/Lower/HLFIR/calls-optional.f90
    flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90
    flang/test/Lower/HLFIR/calls-poly-to-nonpoly.f90
    flang/test/Lower/HLFIR/poly_expr_for_nonpoly_dummy.f90
    flang/test/Lower/OpenMP/copyin.f90
    flang/test/Lower/call-copy-in-out.f90
    flang/test/Lower/components.f90
    flang/test/Lower/dummy-argument-assumed-shape-optional.f90
    flang/test/Lower/dummy-argument-optional-2.f90
    flang/test/Lower/parent-component.f90

Removed: 
    


################################################################################
diff  --git a/flang-rt/include/flang-rt/runtime/tools.h b/flang-rt/include/flang-rt/runtime/tools.h
index 245d2829f8ee5..d856bb206f634 100644
--- a/flang-rt/include/flang-rt/runtime/tools.h
+++ b/flang-rt/include/flang-rt/runtime/tools.h
@@ -534,10 +534,11 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
 // actually modified — while a modifying copy-out never traverses the data
 // more than once nor stores more than the unconditional copy would. The
 // comparison is bitwise, so it is exact when 'from' was originally produced
-// from 'to' by ShallowCopy() (as CopyInAssign() does): unmodified elements
-// compare equal even for NaNs and padding bytes, which a value comparison
-// would misjudge.
-// (Host-only: the sole caller is CopyOutAssign, outside the offload group.)
+// from 'to' by ShallowCopy() (as CopyInAssign() and the copy-in emitted
+// inline by the compiler do): unmodified elements compare equal even for
+// NaNs and padding bytes, which a value comparison would misjudge.
+// (Host-only: the sole caller is CopyOutAssignDirect, outside the offload
+// group.)
 void ShallowCopyModifiedSuffix(const Descriptor &to, const Descriptor &from);
 
 // Ensures that a character string is null-terminated, allocating a /p length +1

diff  --git a/flang-rt/lib/runtime/assign.cpp b/flang-rt/lib/runtime/assign.cpp
index 0ae105d9df671..6f8cfd94e5735 100644
--- a/flang-rt/lib/runtime/assign.cpp
+++ b/flang-rt/lib/runtime/assign.cpp
@@ -834,15 +834,15 @@ void RTDEF(CopyInAssign)(Descriptor &temp, const Descriptor &var,
   ShallowCopy(temp, var);
 }
 
-void RTDEF(CopyOutAssign)(
-    Descriptor *var, Descriptor &temp, const char *sourceFile, int sourceLine) {
-  Terminator terminator{sourceFile, sourceLine};
+void RTDEF(CopyOutAssignDirect)(const Descriptor &var, Descriptor &temp,
+    const char *sourceFile, int sourceLine) {
   // Copyout from the temporary must not cause any finalizations
   // for LHS. The variable must be properly initialized already.
   // Scan for the first bitwise 
diff erence and copy from there to the end
-  // (fused, one pass): the temporary was created as a bitwise copy (see
-  // CopyInAssign above), so it can only 
diff er where the callee modified it,
-  // and an unmodifying copy-out must not store at all. This keeps a
+  // (fused, one pass): the temporary was created as a bitwise copy of the
+  // variable (see CopyInAssign above and the copy-in emitted inline by the
+  // compiler), so it can only 
diff er where the callee modified it, and an
+  // unmodifying copy-out must not store at all. This keeps a
   // compiler-generated copy-out from writing into read-only storage when the
   // effective argument is not definable (e.g., a named constant) and the
   // callee, conformingly, never modified it. From the first 
diff erence
@@ -851,12 +851,18 @@ void RTDEF(CopyOutAssign)(
   // writable anyway.
   // Setting the system environment variable FLANG_RT_COPYOUT_MODIFIED_ONLY=0
   // restores the unconditional copy-out.
+  if (executionEnvironment.copyOutModifiedOnly) {
+    ShallowCopyModifiedSuffix(var, temp);
+  } else {
+    ShallowCopy(var, temp);
+  }
+}
+
+void RTDEF(CopyOutAssign)(
+    Descriptor *var, Descriptor &temp, const char *sourceFile, int sourceLine) {
+  Terminator terminator{sourceFile, sourceLine};
   if (var) {
-    if (executionEnvironment.copyOutModifiedOnly) {
-      ShallowCopyModifiedSuffix(*var, temp);
-    } else {
-      ShallowCopy(*var, temp);
-    }
+    RTNAME(CopyOutAssignDirect)(*var, temp, sourceFile, sourceLine);
   }
   temp.Deallocate();
 }

diff  --git a/flang-rt/lib/runtime/tools.cpp b/flang-rt/lib/runtime/tools.cpp
index 603a646048da8..8152f99c4ad0b 100644
--- a/flang-rt/lib/runtime/tools.cpp
+++ b/flang-rt/lib/runtime/tools.cpp
@@ -368,7 +368,7 @@ RT_API_ATTRS void CreatePartialReductionResult(Descriptor &result,
 RT_OFFLOAD_API_GROUP_END
 
 // The ShallowCopyModifiedSuffix family is deliberately outside the offload
-// API group: its only caller is CopyOutAssign, which is host-only, and
+// API group: its only caller is CopyOutAssignDirect, which is host-only, and
 // instantiating it for the device would only add dead device code.
 // Compares one element bitwise. As in the ShallowCopy* helpers above, the
 // compile-time element size lets the compiler inline the comparison.

diff  --git a/flang/include/flang/Optimizer/Builder/Runtime/Assign.h b/flang/include/flang/Optimizer/Builder/Runtime/Assign.h
index fda941c3261e5..da7bae9654b28 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Assign.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Assign.h
@@ -59,6 +59,9 @@ void genAssignTemporary(fir::FirOpBuilder &builder, mlir::Location loc,
 /// Generate runtime call to "CopyInAssign" runtime API.
 void genCopyInAssign(fir::FirOpBuilder &builder, mlir::Location loc,
                      mlir::Value tempBoxAddr, mlir::Value varBoxAddr);
+/// Generate runtime call to allocation-free "CopyOutAssignDirect" runtime API.
+void genCopyOutAssignDirect(fir::FirOpBuilder &builder, mlir::Location loc,
+                            mlir::Value varBox, mlir::Value tempBoxAddr);
 /// Generate runtime call to "CopyOutAssign" runtime API.
 void genCopyOutAssign(fir::FirOpBuilder &builder, mlir::Location loc,
                       mlir::Value varBoxAddr, mlir::Value tempBoxAddr);

diff  --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index f05554079816c..5e09fe31a91eb 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -1260,14 +1260,14 @@ def hlfir_CopyInOp : hlfir_Op<"copy_in", [MemoryEffects<[MemAlloc]>]> {
     an absent optional and is not contiguous at runtime. When a copy is made this
     operation returns the temporary as first result, otherwise, it returns the
     potentially absent variable storage. The second result indicates if a copy
-    was made.
+    was made, and the third indicates if the returned storage must be freed.
 
-    A descriptor address must be provided for the temporary. This descriptor will
-    be set if a temporary copy was made.
+    A descriptor address must be provided for the temporary. This descriptor is
+    set if a temporary copy is made.
 
     This operation is meant to be used in combination with the hlfir.copy_out
-    operation that takes the address of the descriptor for the temporary, deletes
-    the temporary if it was created, and copies the data back if needed.
+    operation that takes the descriptor address and flags, deletes the temporary
+    if needed, and copies the data back if needed.
     This operation allows passing non contiguous arrays to contiguous dummy
     arguments, which is possible in Fortran procedure references.
 
@@ -1280,7 +1280,7 @@ def hlfir_CopyInOp : hlfir_Op<"copy_in", [MemoryEffects<[MemAlloc]>]> {
                     Arg<AnyReferenceLike, "", [MemWrite]>:$tempBox,
                     Optional<I1>:$var_is_present);
 
-  let results = (outs fir_BaseBoxType, I1);
+  let results = (outs fir_BaseBoxType, I1, I1);
 
   let assemblyFormat = [{
     $var `to` $tempBox (`handle_optional` $var_is_present^)?
@@ -1302,6 +1302,11 @@ def hlfir_CopyInOp : hlfir_Op<"copy_in", [MemoryEffects<[MemAlloc]>]> {
     mlir::Value getWasCopied() {
       return getResult(1);
     }
+
+    /// Get the result indicating if the copied-in storage must be freed.
+    mlir::Value getMustFree() {
+      return getResult(2);
+    }
   }];
 }
 
@@ -1310,19 +1315,20 @@ def hlfir_CopyOutOp : hlfir_Op<"copy_out", [MemoryEffects<[MemFree]>]> {
   let description = [{
     If the variable was copied in a temporary in the related hlfir.copy_in,
     optionally copy back the temporary value to it (that may have been
-    modified between the hlfir.copy_in and hlfir.copy_out). Then deallocate
-    the temporary.
+    modified between the hlfir.copy_in and hlfir.copy_out). Then free the
+    temporary if needed.
     The copy back is done if $var is provided and $was_copied is true.
-    The deallocation of $temp is done if $was_copied is true.
+    The deallocation of $temp is done if $must_free is true.
     $temp must be the descriptor address that was provided to hlfir.copy_in.
   }];
 
   let arguments = (ins Arg<AnyReferenceLike, "", [MemRead]>:$temp,
                        I1:$was_copied,
+                       I1:$must_free,
                        Arg<Optional<fir_BaseBoxType>, "", [MemWrite]>:$var);
 
   let assemblyFormat = [{
-    $temp `,` $was_copied (`to` $var^)?
+    $temp `,` $was_copied `,` $must_free (`to` $var^)?
     attr-dict `:` functional-type(operands, results)
   }];
 }

diff  --git a/flang/include/flang/Optimizer/HLFIR/Passes.td b/flang/include/flang/Optimizer/HLFIR/Passes.td
index ecde8e093c061..2835b3119fa2f 100644
--- a/flang/include/flang/Optimizer/HLFIR/Passes.td
+++ b/flang/include/flang/Optimizer/HLFIR/Passes.td
@@ -14,6 +14,8 @@ def ConvertHLFIRtoFIR : Pass<"convert-hlfir-to-fir", "::mlir::ModuleOp"> {
   let summary = "Lower High-Level FIR to FIR";
   let dependentDialects = [
     "mlir::func::FuncDialect",
+    "mlir::DLTIDialect",
+    "mlir::LLVM::LLVMDialect",
   ];
 }
 

diff  --git a/flang/include/flang/Optimizer/Support/AllocationPolicy.h b/flang/include/flang/Optimizer/Support/AllocationPolicy.h
index fbe3d9c99f0f7..4f9ce3df4c826 100644
--- a/flang/include/flang/Optimizer/Support/AllocationPolicy.h
+++ b/flang/include/flang/Optimizer/Support/AllocationPolicy.h
@@ -21,14 +21,19 @@
 #ifndef FORTRAN_OPTIMIZER_SUPPORT_ALLOCATIONPOLICY_H
 #define FORTRAN_OPTIMIZER_SUPPORT_ALLOCATIONPOLICY_H
 
+#include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "flang/Optimizer/Dialect/Support/KindMapping.h"
+#include "flang/Optimizer/Support/DataLayout.h"
 #include <cstddef>
 #include <cstdint>
 #include <functional>
 #include <optional>
 
 namespace mlir {
+class Location;
 class ModuleOp;
 class Operation;
+class Type;
 } // namespace mlir
 
 namespace fir {
@@ -81,6 +86,22 @@ struct PendingAllocationInfo {
   std::optional<std::int64_t> byteSize;
 };
 
+/// Module-level information needed to compute constant allocation sizes.
+struct AllocationSizeContext {
+  std::optional<mlir::DataLayout> dataLayout;
+  std::optional<fir::KindMapping> kindMap;
+};
+
+/// Gather the module-level information needed to compute allocation sizes.
+AllocationSizeContext getAllocationSizeContext(mlir::Operation *op);
+
+/// Return true if a copy-in buffer should be allocated on the stack. Unlike
+/// general array allocation placement, copy-in buffers with dynamic size are
+/// kept on the heap even under -fstack-arrays.
+bool shouldUseStackForCopyin(mlir::Location loc, mlir::Type sequenceType,
+                             const AllocationPolicy &policy,
+                             const AllocationSizeContext &sizeContext);
+
 /// Facts about a single existing array allocation used to decide its placement.
 struct AllocationInfo : PendingAllocationInfo {
   /// The allocation operation (fir.alloca or fir.allocmem).

diff  --git a/flang/include/flang/Runtime/assign.h b/flang/include/flang/Runtime/assign.h
index 172453334ef15..24be6448d9eb5 100644
--- a/flang/include/flang/Runtime/assign.h
+++ b/flang/include/flang/Runtime/assign.h
@@ -65,14 +65,18 @@ void RTDECL(AssignTemporary)(Descriptor &to, const Descriptor &from,
 // AssignTemporary.
 void RTDECL(CopyInAssign)(Descriptor &temp, const Descriptor &var,
     const char *sourceFile = nullptr, int sourceLine = 0);
-// When "var" is provided, copy "temp" to it assuming "var" is already
-// initialized. The copy is performed only from the first element of "temp"
-// whose bit pattern 
diff ers from the corresponding element of "var" through
-// the last element; when "temp" is bitwise identical to "var", nothing is
-// stored, so a "var" backed by read-only storage is not written to unless
-// it was actually modified. Setting the system environment variable
+// Copy "temp" to the already initialized "var" without deallocating "temp".
+// The copy is performed only from the first element of "temp" whose bit
+// pattern 
diff ers from the corresponding element of "var" through the last
+// element; when "temp" is bitwise identical to "var", nothing is stored, so
+// a "var" backed by read-only storage is not written to unless it was
+// actually modified. Setting the system environment variable
 // FLANG_RT_COPYOUT_MODIFIED_ONLY=0 restores the unconditional whole-object
-// copy. Destroy and deallocate "temp" in all cases.
+// copy.
+void RTDECL(CopyOutAssignDirect)(const Descriptor &var, Descriptor &temp,
+    const char *sourceFile = nullptr, int sourceLine = 0);
+// When "var" is provided, copy "temp" to it as CopyOutAssignDirect does.
+// Destroy and deallocate "temp" in all cases.
 void RTDECL(CopyOutAssign)(Descriptor *var, Descriptor &temp,
     const char *sourceFile = nullptr, int sourceLine = 0);
 // This variant is for assignments to explicit-length CHARACTER left-hand

diff  --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 19678e429249b..8c70c8145b820 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1101,12 +1101,15 @@ namespace {
 struct CallCleanUp {
   struct CopyIn {
     void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
-      hlfir::CopyOutOp::create(builder, loc, tempBox, wasCopied, copyBackVar);
+      hlfir::CopyOutOp::create(builder, loc, tempBox, wasCopied, mustFree,
+                               copyBackVar);
     }
-    // address of the descriptor holding the temp if a temp was created.
+    // Address of the descriptor holding the temp if a temp was created.
     mlir::Value tempBox;
     // Boolean indicating if a copy was made or not.
     mlir::Value wasCopied;
+    // Boolean indicating if the temporary storage must be freed.
+    mlir::Value mustFree;
     // copyBackVar may be null if copy back is not needed.
     mlir::Value copyBackVar;
   };
@@ -1146,9 +1149,9 @@ struct CallCleanUp {
 /// clean-ups to be done after the call.
 struct PreparedDummyArgument {
   void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied,
-                         mlir::Value copyBackVar) {
-    cleanups.emplace_back(
-        CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}});
+                         mlir::Value mustFree, mlir::Value copyBackVar) {
+    cleanups.emplace_back(CallCleanUp{
+        CallCleanUp::CopyIn{tempBox, wasCopied, mustFree, copyBackVar}});
   }
   void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) {
     cleanups.emplace_back(
@@ -1188,6 +1191,7 @@ struct ConditionallyPreparedDummy {
       if (const auto *copyInCleanUp =
               std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {
         thenResultValues.push_back(copyInCleanUp->wasCopied);
+        thenResultValues.push_back(copyInCleanUp->mustFree);
         if (copyInCleanUp->copyBackVar)
           thenResultValues.push_back(copyInCleanUp->copyBackVar);
       } else {
@@ -1244,7 +1248,8 @@ struct ConditionallyPreparedDummy {
         // tempBox is an hlfir.copy_in argument created outside of the
         // fir.if region. It needs not to be threaded as a fir.if result.
         preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox,
-                                        ifOp.getResults()[1], copyBackVar);
+                                        ifOp.getResults()[1],
+                                        ifOp.getResults()[2], copyBackVar);
       } else {
         preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1],
                                                ifOp.getResults()[2]);
@@ -1455,16 +1460,13 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity {
     auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType());
     assert(baseBoxTy && "expect non simply contiguous variables to be boxes");
-    // Create allocatable descriptor for the potential temporary.
-    mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr(
-        fir::BaseBoxType::Attribute::Allocatable);
-    mlir::Value tempBox = builder.createTemporary(loc, tempBoxType);
+    mlir::Value tempBox = builder.createTemporary(loc, var.getType());
     auto copyIn = hlfir::CopyInOp::create(builder, loc, var, tempBox,
                                           /*var_is_present=*/mlir::Value{});
     // Register the copy-out after the call.
-    preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(),
-                                    doCopyOut ? copyIn.getVar()
-                                              : mlir::Value{});
+    preparedDummy.pushCopyInCleanUp(
+        copyIn.getTempBox(), copyIn.getWasCopied(), copyIn.getMustFree(),
+        doCopyOut ? copyIn.getVar() : mlir::Value{});
     return hlfir::Entity{copyIn.getCopiedIn()};
   };
 

diff  --git a/flang/lib/Optimizer/Builder/Runtime/Assign.cpp b/flang/lib/Optimizer/Builder/Runtime/Assign.cpp
index fc9c6b0eb51e3..aaa90dcd51092 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Assign.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Assign.cpp
@@ -82,6 +82,21 @@ void fir::runtime::genCopyInAssign(fir::FirOpBuilder &builder,
   fir::CallOp::create(builder, loc, func, args);
 }
 
+void fir::runtime::genCopyOutAssignDirect(fir::FirOpBuilder &builder,
+                                          mlir::Location loc,
+                                          mlir::Value destBox,
+                                          mlir::Value sourceBoxAddr) {
+  auto func =
+      fir::runtime::getRuntimeFunc<mkRTKey(CopyOutAssignDirect)>(loc, builder);
+  auto fTy = func.getFunctionType();
+  auto sourceFile = fir::factory::locationToFilename(builder, loc);
+  auto sourceLine =
+      fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
+  auto args = fir::runtime::createArguments(
+      builder, loc, fTy, destBox, sourceBoxAddr, sourceFile, sourceLine);
+  fir::CallOp::create(builder, loc, func, args);
+}
+
 void fir::runtime::genCopyOutAssign(fir::FirOpBuilder &builder,
                                     mlir::Location loc, mlir::Value destBox,
                                     mlir::Value sourceBox) {

diff  --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 53c9b6f500d05..9503d64bd5d37 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -1949,9 +1949,10 @@ llvm::LogicalResult hlfir::DestroyOp::verify() {
 
 void hlfir::CopyInOp::build(mlir::OpBuilder &builder,
                             mlir::OperationState &odsState, mlir::Value var,
-                            mlir::Value tempBox, mlir::Value var_is_present) {
-  return build(builder, odsState, {var.getType(), builder.getI1Type()}, var,
-               tempBox, var_is_present);
+                            mlir::Value temp_box, mlir::Value var_is_present) {
+  return build(builder, odsState,
+               {var.getType(), builder.getI1Type(), builder.getI1Type()}, var,
+               temp_box, var_is_present);
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 2be94d40ebd50..3b02ea5b1740d 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -15,6 +15,7 @@
 #include "flang/Optimizer/Builder/Runtime/Assign.h"
 #include "flang/Optimizer/Builder/Runtime/Derived.h"
 #include "flang/Optimizer/Builder/Runtime/Inquiry.h"
+#include "flang/Optimizer/Builder/Runtime/Transformational.h"
 #include "flang/Optimizer/Builder/Todo.h"
 #include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
@@ -22,6 +23,11 @@
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "flang/Optimizer/HLFIR/Passes.h"
+#include "flang/Optimizer/Support/AllocationPolicy.h"
+#include "flang/Optimizer/Support/DataLayout.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/Transforms/DialectConversion.h"
 #include "llvm/ADT/SmallSet.h"
 
@@ -228,61 +234,115 @@ class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
 
 class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
 public:
-  explicit CopyInOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {}
+  CopyInOpConversion(mlir::MLIRContext *ctx,
+                     const fir::AllocationPolicy &policy,
+                     const fir::AllocationSizeContext &sizeContext)
+      : OpRewritePattern{ctx}, policy{policy}, sizeContext{sizeContext} {}
 
   struct CopyInResult {
     mlir::Value addr;
     mlir::Value wasCopied;
+    bool mustFree;
   };
 
-  static CopyInResult genNonOptionalCopyIn(mlir::Location loc,
-                                           fir::FirOpBuilder &builder,
-                                           hlfir::CopyInOp copyInOp) {
+  CopyInResult genNonOptionalCopyIn(mlir::Location loc,
+                                    fir::FirOpBuilder &builder,
+                                    hlfir::CopyInOp copyInOp) const {
     mlir::Value inputVariable = copyInOp.getVar();
     mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();
     mlir::Value isContiguous =
         fir::runtime::genIsContiguous(builder, loc, inputVariable);
+    mlir::Value wasCopied = builder.genNot(loc, isContiguous);
+    const bool isAssumedRank =
+        mlir::cast<fir::BaseBoxType>(resultAddrType).isAssumedRank();
+    if (isAssumedRank || fir::isPolymorphicType(inputVariable.getType())) {
+      mlir::Value tempBox = copyInOp.getTempBox();
+      mlir::Value addr =
+          builder
+              .genIfOp(loc, {resultAddrType}, isContiguous,
+                       /*withElseRegion=*/true)
+              .genThen(
+                  [&] { fir::ResultOp::create(builder, loc, inputVariable); })
+              .genElse([&] {
+                fir::runtime::genCopyInAssign(builder, loc, tempBox,
+                                              inputVariable);
+                mlir::Value copy = fir::LoadOp::create(builder, loc, tempBox);
+                if (isAssumedRank)
+                  copy = fir::ReboxAssumedRankOp::create(
+                      builder, loc, resultAddrType, copy,
+                      fir::LowerBoundModifierAttribute::Preserve);
+                else
+                  copy = fir::ReboxOp::create(
+                      builder, loc, resultAddrType, copy,
+                      /*shape=*/mlir::Value{}, /*slice=*/mlir::Value{});
+                fir::ResultOp::create(builder, loc, copy);
+              })
+              .getResults()[0];
+      return {addr, wasCopied, /*mustFree=*/true};
+    }
+
+    hlfir::Entity inputEntity{inputVariable};
+    inputEntity =
+        hlfir::derefPointersAndAllocatables(loc, builder, inputEntity);
+    mlir::Type sequenceType = inputEntity.getElementOrSequenceType();
+    mlir::Value shape = hlfir::genShape(loc, builder, inputEntity);
+    llvm::SmallVector<mlir::Value> extents =
+        hlfir::getIndexExtents(loc, builder, shape);
+    llvm::SmallVector<mlir::Value> typeParams;
+    hlfir::genLengthParameters(loc, builder, inputEntity, typeParams);
+    const bool useStack =
+        fir::shouldUseStackForCopyin(loc, sequenceType, policy, sizeContext);
+
     mlir::Value addr =
         builder
             .genIfOp(loc, {resultAddrType}, isContiguous,
                      /*withElseRegion=*/true)
             .genThen(
-                [&]() { fir::ResultOp::create(builder, loc, inputVariable); })
+                [&] { fir::ResultOp::create(builder, loc, inputVariable); })
             .genElse([&] {
-              // Create temporary on the heap. Note that the runtime is used and
-              // that is desired: since the data copy happens under a runtime
-              // check (for IsContiguous) the copy loops can hardly provide any
-              // value to optimizations, instead, the optimizer just wastes
-              // compilation time on these loops.
-              mlir::Value temp = copyInOp.getTempBox();
-              fir::runtime::genCopyInAssign(builder, loc, temp, inputVariable);
-              mlir::Value copy = fir::LoadOp::create(builder, loc, temp);
-              // Get rid of allocatable flag in the fir.box.
-              if (mlir::cast<fir::BaseBoxType>(resultAddrType).isAssumedRank())
-                copy = fir::ReboxAssumedRankOp::create(
-                    builder, loc, resultAddrType, copy,
-                    fir::LowerBoundModifierAttribute::Preserve);
-              else
-                copy = fir::ReboxOp::create(builder, loc, resultAddrType, copy,
-                                            /*shape=*/mlir::Value{},
-                                            /*slice=*/mlir::Value{});
+              llvm::StringRef tmpName{".tmp.copy_in"};
+              mlir::Value allocation =
+                  useStack
+                      ? builder.createTemporary(loc, sequenceType, tmpName,
+                                                extents, typeParams)
+                      : builder.createHeapTemporary(loc, sequenceType, tmpName,
+                                                    extents, typeParams);
+              mlir::Value declared = fir::FirOpBuilder::genTempDeclareOp(
+                  builder, loc, allocation, tmpName, shape, typeParams,
+                  fir::FortranVariableFlagsAttr{});
+
+              bool useDynamicType =
+                  fir::isBoxedRecordType(inputVariable.getType());
+              mlir::Type tempBoxType = fir::wrapInClassOrBoxType(
+                  sequenceType, /*isPolymorphic=*/useDynamicType);
+              mlir::Value tempBox = builder.createBox(
+                  loc, tempBoxType, declared, shape, /*slice=*/nullptr,
+                  typeParams, useDynamicType ? inputVariable : nullptr);
+              fir::runtime::genShallowCopy(builder, loc, tempBox, inputVariable,
+                                           /*resultIsAllocated=*/true);
+              mlir::Value copy = fir::ReboxOp::create(
+                  builder, loc, resultAddrType, tempBox,
+                  /*shape=*/mlir::Value{}, /*slice=*/mlir::Value{});
+              fir::StoreOp::create(builder, loc, copy, copyInOp.getTempBox());
               fir::ResultOp::create(builder, loc, copy);
             })
             .getResults()[0];
-    return {addr, builder.genNot(loc, isContiguous)};
+
+    return {addr, wasCopied, /*mustFree=*/!useStack};
   }
 
-  static CopyInResult genOptionalCopyIn(mlir::Location loc,
-                                        fir::FirOpBuilder &builder,
-                                        hlfir::CopyInOp copyInOp) {
+  CopyInResult genOptionalCopyIn(mlir::Location loc, fir::FirOpBuilder &builder,
+                                 hlfir::CopyInOp copyInOp) const {
     mlir::Type resultAddrType = copyInOp.getCopiedIn().getType();
     mlir::Value isPresent = copyInOp.getVarIsPresent();
+    bool mustFree = false;
     auto res =
         builder
             .genIfOp(loc, {resultAddrType, builder.getI1Type()}, isPresent,
                      /*withElseRegion=*/true)
             .genThen([&]() {
               CopyInResult res = genNonOptionalCopyIn(loc, builder, copyInOp);
+              mustFree = res.mustFree;
               fir::ResultOp::create(builder, loc,
                                     mlir::ValueRange{res.addr, res.wasCopied});
             })
@@ -293,7 +353,7 @@ class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
                                     mlir::ValueRange{absent, isPresent});
             })
             .getResults();
-    return {res[0], res[1]};
+    return {res[0], res[1], mustFree};
   }
 
   llvm::LogicalResult
@@ -304,9 +364,18 @@ class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
     CopyInResult result = copyInOp.getVarIsPresent()
                               ? genOptionalCopyIn(loc, builder, copyInOp)
                               : genNonOptionalCopyIn(loc, builder, copyInOp);
-    rewriter.replaceOp(copyInOp, {result.addr, result.wasCopied});
+    // Heap temps are only created when a copy is made. Keep mustFree false
+    // on the stack path (constant) and equal to wasCopied on the heap path
+    // so copy-out can free on mustFree alone.
+    mlir::Value mustFree =
+        result.mustFree ? result.wasCopied : builder.createBool(loc, false);
+    rewriter.replaceOp(copyInOp, {result.addr, result.wasCopied, mustFree});
     return mlir::success();
   }
+
+private:
+  fir::AllocationPolicy policy;
+  const fir::AllocationSizeContext &sizeContext;
 };
 
 class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> {
@@ -320,29 +389,22 @@ class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> {
     mlir::Location loc = copyOutOp.getLoc();
     fir::FirOpBuilder builder(rewriter, copyOutOp.getOperation());
 
-    builder.genIfThen(loc, copyOutOp.getWasCopied())
-        .genThen([&]() {
-          mlir::Value temp = copyOutOp.getTemp();
-          mlir::Value varMutableBox;
-          // Generate CopyOutAssign runtime call.
-          if (mlir::Value var = copyOutOp.getVar()) {
-            // Set the variable descriptor pointer in order to copy data from
-            // the temporary to the actualArg. Note that in case the actual
-            // argument is ALLOCATABLE/POINTER the CopyOutAssign()
-            // implementation should not engage its reallocation, because the
-            // temporary is rank, shape and type compatible with it. Moreover,
-            // CopyOutAssign() guarantees that there will be no finalization for
-            // the LHS even if it is of a derived type with finalization.
-            varMutableBox = builder.createTemporary(loc, var.getType());
-            fir::StoreOp::create(builder, loc, var, varMutableBox);
-          } else {
-            // Even when there is no need to copy back the data (e.g., the dummy
-            // argument was intent(in), CopyOutAssign is called to
-            // destroy/deallocate the temporary.
-            varMutableBox = fir::ZeroOp::create(builder, loc, temp.getType());
-          }
-          fir::runtime::genCopyOutAssign(builder, loc, varMutableBox,
-                                         copyOutOp.getTemp());
+    if (mlir::Value var = copyOutOp.getVar())
+      builder.genIfThen(loc, copyOutOp.getWasCopied())
+          .genThen([&] {
+            fir::runtime::genCopyOutAssignDirect(builder, loc, var,
+                                                 copyOutOp.getTemp());
+          })
+          .end();
+    builder.genIfThen(loc, copyOutOp.getMustFree())
+        .genThen([&] {
+          mlir::Value temp =
+              fir::LoadOp::create(builder, loc, copyOutOp.getTemp());
+          mlir::Value tempAddr = fir::BoxAddrOp::create(builder, loc, temp);
+          mlir::Value heapAddr = fir::ConvertOp::create(
+              builder, loc, fir::HeapType::get(builder.getIntegerType(8)),
+              tempAddr);
+          fir::FreeMemOp::create(builder, loc, heapAddr);
         })
         .end();
     rewriter.eraseOp(copyOutOp);
@@ -851,8 +913,13 @@ class ConvertHLFIRtoFIR
     // generate the signatures in a thread safe way.
     auto module = this->getOperation();
     auto *context = &getContext();
+    fir::AllocationPolicy allocationPolicy = fir::getAllocationPolicy(module);
+    fir::AllocationSizeContext allocationSizeContext =
+        fir::getAllocationSizeContext(module);
     mlir::RewritePatternSet patterns(context);
-    patterns.insert<AssignOpConversion, CopyInOpConversion, CopyOutOpConversion,
+    patterns.insert<CopyInOpConversion>(context, allocationPolicy,
+                                        allocationSizeContext);
+    patterns.insert<AssignOpConversion, CopyOutOpConversion,
                     DeclareOpConversion, DesignateOpConversion,
                     GetExtentOpConversion, NoReassocOpConversion,
                     NullOpConversion, ParentComponentOpConversion>(context);

diff  --git a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRCopy.cpp b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRCopy.cpp
index 4cb7403370037..cf401db47a86e 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRCopy.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRCopy.cpp
@@ -24,7 +24,6 @@
 #include "flang/Optimizer/HLFIR/Passes.h"
 #include "flang/Optimizer/OpenMP/Passes.h"
 #include "flang/Optimizer/Support/AllocationPolicy.h"
-#include "flang/Optimizer/Support/DataLayout.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/DLTI/DLTI.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
@@ -45,32 +44,11 @@ static llvm::cl::opt<bool> noInlineHLFIRCopy(
     llvm::cl::init(false));
 
 namespace {
-/// Everything needed to compute the constant byte size of a buffer, gathered
-/// once by the pass since it is module-level information.
-struct SizeContext {
-  std::optional<mlir::DataLayout> dataLayout;
-  std::optional<fir::KindMapping> kindMap;
-};
-
-/// Gather the module level information needed to compute buffer sizes. Without
-/// a data layout no size can be computed, and all the buffers are then left on
-/// the heap.
-static SizeContext getSizeContext(mlir::Operation *op) {
-  auto module = mlir::dyn_cast<mlir::ModuleOp>(op);
-  if (!module)
-    module = op->getParentOfType<mlir::ModuleOp>();
-  if (!module)
-    return SizeContext{std::nullopt, std::nullopt};
-  return SizeContext{fir::support::getOrSetMLIRDataLayout(
-                         module, /*allowDefaultLayout=*/false),
-                     fir::getKindMapping(module)};
-}
-
 class InlineCopyInConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
 public:
   InlineCopyInConversion(mlir::MLIRContext *context,
                          const fir::AllocationPolicy &policy,
-                         const SizeContext &sizeContext)
+                         const fir::AllocationSizeContext &sizeContext)
       : mlir::OpRewritePattern<hlfir::CopyInOp>(context), policy(policy),
         sizeContext(sizeContext) {}
 
@@ -79,40 +57,10 @@ class InlineCopyInConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
                   mlir::PatternRewriter &rewriter) const override;
 
 private:
-  /// Return true if the copy-in buffer of type \p sequenceType should be
-  /// allocated on the stack rather than on the heap.
-  bool shouldUseStack(mlir::Location loc, mlir::Type sequenceType) const;
-
   fir::AllocationPolicy policy;
-  const SizeContext &sizeContext;
+  const fir::AllocationSizeContext &sizeContext;
 };
 
-bool InlineCopyInConversion::shouldUseStack(mlir::Location loc,
-                                            mlir::Type sequenceType) const {
-  // Only buffers with a compile-time constant size are considered. A buffer
-  // with a runtime size would need stack save/restore to avoid growing the
-  // stack when the copy-in is inside a loop. There is also little to gain: for
-  // a big buffer the element-per-element copy costs much more than the
-  // allocation itself.
-  if (fir::hasDynamicSize(sequenceType))
-    return false;
-  if (!sizeContext.dataLayout || !sizeContext.kindMap)
-    return false;
-  auto sizeAndAlignment = fir::getTypeSizeAndAlignment(
-      loc, sequenceType, *sizeContext.dataLayout, *sizeContext.kindMap);
-  if (!sizeAndAlignment)
-    return false;
-
-  fir::PendingAllocationInfo info;
-  info.isTemporary = true;
-  info.isDynamic = false;
-  info.byteSize = static_cast<std::int64_t>(sizeAndAlignment->first);
-  // The per-function stack budget is not tracked here: the
-  // allocation-placement pass sees the fir.alloca generated below and can
-  // still move it back to the heap if the budget turns out to be exceeded.
-  return fir::shouldAllocateOnStack(info, policy, /*stackBytesUsed=*/0);
-}
-
 // Inline a copy_out operation (deallocation only — no copy-back).
 // Generates: if (wasCopied) { freemem(temp) }
 static void inlineCopyOut(fir::FirOpBuilder &builder, mlir::Location loc,
@@ -179,7 +127,8 @@ InlineCopyInConversion::matchAndRewrite(hlfir::CopyInOp copyIn,
 
   // Decide where the buffer will live before creating it, so that the matching
   // kind of allocation and deallocation is generated.
-  const bool useStack = shouldUseStack(loc, sequenceType);
+  const bool useStack =
+      fir::shouldUseStackForCopyin(loc, sequenceType, policy, sizeContext);
 
   mlir::Value isContiguous =
       fir::IsContiguousBoxOp::create(builder, loc, inputVariable);
@@ -260,7 +209,9 @@ InlineCopyInConversion::matchAndRewrite(hlfir::CopyInOp copyIn,
   // Erase the copyOut since we've inlined it
   rewriter.eraseOp(copyOut);
 
-  rewriter.replaceOp(copyIn, {resultBox, builder.genNot(loc, isContiguous)});
+  rewriter.replaceOp(copyIn,
+                     {resultBox, builder.genNot(loc, isContiguous),
+                      useStack ? builder.createBool(loc, false) : wasCopied});
   return mlir::success();
 }
 
@@ -285,10 +236,10 @@ class InlineHLFIRCopyPass
     // beneficial. Runtime-sized buffers additionally need stack save/restore
     // to avoid growing the stack when the copy-in sits in a loop.
     fir::AllocationPolicy policy = fir::getAllocationPolicy(getOperation());
-    policy.stackArrays = false;
     fir::overrideIfExplicitlySet(policy.smallArrayThresholdBytes,
                                  smallArrayThresholdBytes);
-    const SizeContext sizeContext = getSizeContext(getOperation());
+    const fir::AllocationSizeContext sizeContext =
+        fir::getAllocationSizeContext(getOperation());
 
     mlir::RewritePatternSet patterns(context);
     if (!noInlineHLFIRCopy) {

diff  --git a/flang/lib/Optimizer/Support/AllocationPolicy.cpp b/flang/lib/Optimizer/Support/AllocationPolicy.cpp
index fa0036db9758b..32a64b50b6f51 100644
--- a/flang/lib/Optimizer/Support/AllocationPolicy.cpp
+++ b/flang/lib/Optimizer/Support/AllocationPolicy.cpp
@@ -12,6 +12,7 @@
 
 #include "flang/Optimizer/Support/AllocationPolicy.h"
 #include "flang/Optimizer/Dialect/FIRAttr.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "llvm/Support/CommandLine.h"
 
@@ -33,6 +34,35 @@ static llvm::cl::opt<std::uint64_t> allocationPlacementStackLimit(
     llvm::cl::init(fir::AllocationPolicy::totalStackLimitBytesDefault),
     llvm::cl::Hidden);
 
+fir::AllocationSizeContext fir::getAllocationSizeContext(mlir::Operation *op) {
+  auto module = mlir::dyn_cast<mlir::ModuleOp>(op);
+  if (!module)
+    module = op->getParentOfType<mlir::ModuleOp>();
+  if (!module)
+    return {std::nullopt, std::nullopt};
+  return {fir::support::getOrSetMLIRDataLayout(module,
+                                               /*allowDefaultLayout=*/false),
+          fir::getKindMapping(module)};
+}
+
+bool fir::shouldUseStackForCopyin(mlir::Location loc, mlir::Type sequenceType,
+                                  const AllocationPolicy &allocationPolicy,
+                                  const AllocationSizeContext &sizeContext) {
+  if (fir::hasDynamicSize(sequenceType) || !sizeContext.dataLayout ||
+      !sizeContext.kindMap)
+    return false;
+  auto sizeAndAlignment = fir::getTypeSizeAndAlignment(
+      loc, sequenceType, *sizeContext.dataLayout, *sizeContext.kindMap);
+  if (!sizeAndAlignment)
+    return false;
+  PendingAllocationInfo info{
+      /*isTemporary=*/true, /*isDynamic=*/false,
+      static_cast<std::int64_t>(sizeAndAlignment->first)};
+  AllocationPolicy copyInPolicy = allocationPolicy;
+  copyInPolicy.stackArrays = false;
+  return shouldAllocateOnStack(info, copyInPolicy, /*stackBytesUsed=*/0);
+}
+
 bool fir::shouldAllocateOnStack(const PendingAllocationInfo &info,
                                 const AllocationPolicy &policy,
                                 std::size_t stackBytesUsed) {

diff  --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt
index bdd193906c51f..9f1aa3c24257b 100644
--- a/flang/lib/Optimizer/Support/CMakeLists.txt
+++ b/flang/lib/Optimizer/Support/CMakeLists.txt
@@ -12,6 +12,7 @@ add_flang_library(FIRSupport
 
   LINK_LIBS
   FIRDialect
+  FIRDialectSupport
   MIFDialect
 
   LINK_COMPONENTS

diff  --git a/flang/test/Fir/allocation-policy-pipeline.fir b/flang/test/Fir/allocation-policy-pipeline.fir
index 585a671cf1ca0..db2396eb621bc 100644
--- a/flang/test/Fir/allocation-policy-pipeline.fir
+++ b/flang/test/Fir/allocation-policy-pipeline.fir
@@ -13,6 +13,8 @@
 // RUN: fir-opt --inline-hlfir-copy="small-array-threshold=4096" \
 // RUN:   --allocation-placement="small-array-threshold=4096" %s \
 // RUN:   | FileCheck %s --check-prefix=BIG
+// RUN: fir-opt --convert-hlfir-to-fir %s \
+// RUN:   | FileCheck %s --check-prefix=CODEGEN
 
 module attributes {fir.allocation_policy =
                        #fir.allocation_policy<stack_arrays = false,
@@ -22,6 +24,21 @@ module attributes {fir.allocation_policy =
                    llvm.data_layout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"} {
 
 func.func private @callee100(!fir.ref<!fir.array<100xf64>>) -> ()
+func.func private @callee8(!fir.ref<!fir.array<8xf64>>) -> ()
+
+// CODEGEN-LABEL: func.func @small_copy_in_buffer
+// CODEGEN:         fir.alloca !fir.array<8xf64>
+// CODEGEN:         fir.call @_FortranAShallowCopyDirect
+// CODEGEN:         fir.call @callee8
+// CODEGEN-NOT:     fir.allocmem
+func.func @small_copy_in_buffer(%arg0: !fir.box<!fir.array<8xf64>>) {
+  %0 = fir.alloca !fir.box<!fir.array<8xf64>>
+  %1:3 = hlfir.copy_in %arg0 to %0 : (!fir.box<!fir.array<8xf64>>, !fir.ref<!fir.box<!fir.array<8xf64>>>) -> (!fir.box<!fir.array<8xf64>>, i1, i1)
+  %2 = fir.box_addr %1#0 : (!fir.box<!fir.array<8xf64>>) -> !fir.ref<!fir.array<8xf64>>
+  fir.call @callee8(%2) : (!fir.ref<!fir.array<8xf64>>) -> ()
+  hlfir.copy_out %0, %1#1, %1#2 : (!fir.ref<!fir.box<!fir.array<8xf64>>>, i1, i1) -> ()
+  return
+}
 
 // SMALL-LABEL:   func.func @copy_in_buffer
 // SMALL:           fir.allocmem !fir.array<100xf64> {bindc_name = ".tmp.copy_in"
@@ -32,18 +49,50 @@ func.func private @callee100(!fir.ref<!fir.array<100xf64>>) -> ()
 // BIG:             fir.alloca !fir.array<100xf64> {bindc_name = ".tmp.copy_in"
 // BIG-NOT:         fir.allocmem
 // BIG-NOT:         fir.freemem
+// CODEGEN-LABEL: func.func @copy_in_buffer
+// CODEGEN-COUNT-1: fir.alloca !fir.box
+// CODEGEN-NOT:     fir.alloca !fir.box
+// CODEGEN:         fir.allocmem !fir.array<100xf64>
+// CODEGEN:         fir.embox
+// CODEGEN:         fir.call @_FortranAShallowCopyDirect
+// CODEGEN:         fir.store %{{.*}} to %{{.*}} : !fir.ref<!fir.box<!fir.array<100xf64>>>
+// CODEGEN-NOT:     fir.call @_FortranACopyInAssign
+// CODEGEN:         fir.call @callee100
+// CODEGEN:         fir.if
+// CODEGEN:         fir.freemem
+// CODEGEN-NOT:     fir.call @_FortranACopyOutAssign
 func.func @copy_in_buffer(%arg0: !fir.box<!fir.array<?x?xf64>>, %arg1: !fir.ref<i32>) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<100xf64>>>
+  %0 = fir.alloca !fir.box<!fir.array<100xf64>>
   %c1 = arith.constant 1 : index
   %c100 = arith.constant 100 : index
   %1 = fir.load %arg1 : !fir.ref<i32>
   %2 = fir.convert %1 : (i32) -> i64
   %3 = fir.shape %c100 : (index) -> !fir.shape<1>
   %4 = hlfir.designate %arg0 (%2, %c1:%c100:%c1)  shape %3 : (!fir.box<!fir.array<?x?xf64>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<100xf64>>
-  %5:2 = hlfir.copy_in %4 to %0 : (!fir.box<!fir.array<100xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<100xf64>>>>) -> (!fir.box<!fir.array<100xf64>>, i1)
+  %5:3 = hlfir.copy_in %4 to %0 : (!fir.box<!fir.array<100xf64>>, !fir.ref<!fir.box<!fir.array<100xf64>>>) -> (!fir.box<!fir.array<100xf64>>, i1, i1)
   %6 = fir.box_addr %5#0 : (!fir.box<!fir.array<100xf64>>) -> !fir.ref<!fir.array<100xf64>>
   fir.call @callee100(%6) : (!fir.ref<!fir.array<100xf64>>) -> ()
-  hlfir.copy_out %0, %5#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<100xf64>>>>, i1) -> ()
+  hlfir.copy_out %0, %5#1, %5#2 : (!fir.ref<!fir.box<!fir.array<100xf64>>>, i1, i1) -> ()
+  return
+}
+
+// Copy-back uses a non-allocating runtime entry point before freeing the
+// compiler-allocated buffer.
+// CODEGEN-LABEL: func.func @copy_in_out_buffer
+// CODEGEN:         fir.allocmem !fir.array<100xf64>
+// CODEGEN:         fir.call @_FortranAShallowCopyDirect
+// CODEGEN:         fir.call @callee100
+// CODEGEN:         fir.if
+// CODEGEN:           fir.call @_FortranACopyOutAssignDirect
+// CODEGEN:         fir.if
+// CODEGEN:           fir.freemem
+// CODEGEN-NOT:     fir.call @_FortranACopy{{In|Out}}Assign
+func.func @copy_in_out_buffer(%arg0: !fir.box<!fir.array<100xf64>>) {
+  %0 = fir.alloca !fir.box<!fir.array<100xf64>>
+  %1:3 = hlfir.copy_in %arg0 to %0 : (!fir.box<!fir.array<100xf64>>, !fir.ref<!fir.box<!fir.array<100xf64>>>) -> (!fir.box<!fir.array<100xf64>>, i1, i1)
+  %2 = fir.box_addr %1#0 : (!fir.box<!fir.array<100xf64>>) -> !fir.ref<!fir.array<100xf64>>
+  fir.call @callee100(%2) : (!fir.ref<!fir.array<100xf64>>) -> ()
+  hlfir.copy_out %0, %1#1, %1#2 to %arg0 : (!fir.ref<!fir.box<!fir.array<100xf64>>>, i1, i1, !fir.box<!fir.array<100xf64>>) -> ()
   return
 }
 

diff  --git a/flang/test/HLFIR/assumed-type-actual-args.f90 b/flang/test/HLFIR/assumed-type-actual-args.f90
index fde7965e76e3a..47e217405cb5e 100644
--- a/flang/test/HLFIR/assumed-type-actual-args.f90
+++ b/flang/test/HLFIR/assumed-type-actual-args.f90
@@ -132,10 +132,10 @@ subroutine s5b(x)
 ! CHECK-SAME:                        %[[VAL_0:.*]]: !fir.box<!fir.array<?xnone>> {fir.bindc_name = "x"}) {
 ! CHECK:           %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[DSCOPE]] arg {{[0-9]+}} {uniq_name = "_QFtest4Ex"} : (!fir.box<!fir.array<?xnone>>, !fir.dscope) -> (!fir.box<!fir.array<?xnone>>, !fir.box<!fir.array<?xnone>>)
-! CHECK:           %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>) -> (!fir.box<!fir.array<?xnone>>, i1)
+! CHECK:           %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.array<?xnone>>>) -> (!fir.box<!fir.array<?xnone>>, i1, i1)
 ! CHECK:           %[[VAL_3:.*]] = fir.box_addr %[[VAL_2]]#0 : (!fir.box<!fir.array<?xnone>>) -> !fir.ref<!fir.array<?xnone>>
 ! CHECK:           fir.call @_QPs4(%[[VAL_3]]) fastmath<contract> : (!fir.ref<!fir.array<?xnone>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>, i1, !fir.box<!fir.array<?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.array<?xnone>>>, i1, i1, !fir.box<!fir.array<?xnone>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 
@@ -144,17 +144,18 @@ subroutine s5b(x)
 ! CHECK:           %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[DSCOPE]] arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFtest3bEx"} : (!fir.box<!fir.array<?xnone>>, !fir.dscope) -> (!fir.box<!fir.array<?xnone>>, !fir.box<!fir.array<?xnone>>)
 ! CHECK:           %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#0 : (!fir.box<!fir.array<?xnone>>) -> i1
-! CHECK:           %[[VAL_3:.*]]:3 = fir.if %[[VAL_2]] -> (!fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>) {
-! CHECK:             %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>) -> (!fir.box<!fir.array<?xnone>>, i1)
-! CHECK:             fir.result %[[VAL_4]]#0, %[[VAL_4]]#1, %[[VAL_1]]#0 : !fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:           %[[VAL_3:.*]]:4 = fir.if %[[VAL_2]] -> (!fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>) {
+! CHECK:             %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.array<?xnone>>>) -> (!fir.box<!fir.array<?xnone>>, i1, i1)
+! CHECK:             fir.result %[[VAL_4]]#0, %[[VAL_4]]#1, %[[VAL_4]]#2, %[[VAL_1]]#0 : !fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           } else {
 ! CHECK:             %[[VAL_6:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
 ! CHECK:             %[[VAL_7:.*]] = arith.constant false
-! CHECK:             %[[VAL_8:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
-! CHECK:             fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]] : !fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:             %[[VAL_8:.*]] = arith.constant false
+! CHECK:             %[[VAL_9:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
+! CHECK:             fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           }
-! CHECK:           fir.call @_QPs3b(%[[VAL_9:.*]]#0) fastmath<contract> : (!fir.box<!fir.array<?xnone>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_9]]#1 to %[[VAL_9]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>, i1, !fir.box<!fir.array<?xnone>>) -> ()
+! CHECK:           fir.call @_QPs3b(%[[VAL_11:.*]]#0) fastmath<contract> : (!fir.box<!fir.array<?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_11]]#1, %[[VAL_11]]#2 to %[[VAL_11]]#3 : (!fir.ref<!fir.box<!fir.array<?xnone>>>, i1, i1, !fir.box<!fir.array<?xnone>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 
@@ -163,18 +164,19 @@ subroutine s5b(x)
 ! CHECK:           %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[DSCOPE]] arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFtest4bEx"} : (!fir.box<!fir.array<?xnone>>, !fir.dscope) -> (!fir.box<!fir.array<?xnone>>, !fir.box<!fir.array<?xnone>>)
 ! CHECK:           %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#0 : (!fir.box<!fir.array<?xnone>>) -> i1
-! CHECK:           %[[VAL_3:.*]]:3 = fir.if %[[VAL_2]] -> (!fir.ref<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>) {
-! CHECK:             %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>) -> (!fir.box<!fir.array<?xnone>>, i1)
+! CHECK:           %[[VAL_3:.*]]:4 = fir.if %[[VAL_2]] -> (!fir.ref<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>) {
+! CHECK:             %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.array<?xnone>>>) -> (!fir.box<!fir.array<?xnone>>, i1, i1)
 ! CHECK:             %[[VAL_5:.*]] = fir.box_addr %[[VAL_4]]#0 : (!fir.box<!fir.array<?xnone>>) -> !fir.ref<!fir.array<?xnone>>
-! CHECK:             fir.result %[[VAL_5]], %[[VAL_4]]#1, %[[VAL_1]]#0 : !fir.ref<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:             fir.result %[[VAL_5]], %[[VAL_4]]#1, %[[VAL_4]]#2, %[[VAL_1]]#0 : !fir.ref<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           } else {
 ! CHECK:             %[[VAL_7:.*]] = fir.absent !fir.ref<!fir.array<?xnone>>
 ! CHECK:             %[[VAL_8:.*]] = arith.constant false
-! CHECK:             %[[VAL_9:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
-! CHECK:             fir.result %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !fir.ref<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:             %[[VAL_9:.*]] = arith.constant false
+! CHECK:             %[[VAL_10:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
+! CHECK:             fir.result %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !fir.ref<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           }
-! CHECK:           fir.call @_QPs4b(%[[VAL_10:.*]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xnone>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_10]]#1 to %[[VAL_10]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>, i1, !fir.box<!fir.array<?xnone>>) -> ()
+! CHECK:           fir.call @_QPs4b(%[[VAL_12:.*]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_12]]#1, %[[VAL_12]]#2 to %[[VAL_12]]#3 : (!fir.ref<!fir.box<!fir.array<?xnone>>>, i1, i1, !fir.box<!fir.array<?xnone>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 
@@ -217,17 +219,18 @@ subroutine s5b(x)
 ! CHECK:           %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[DSCOPE]] arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFtest5bEx"} : (!fir.box<!fir.array<?xnone>>, !fir.dscope) -> (!fir.box<!fir.array<?xnone>>, !fir.box<!fir.array<?xnone>>)
 ! CHECK:           %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#0 : (!fir.box<!fir.array<?xnone>>) -> i1
-! CHECK:           %[[VAL_3:.*]]:3 = fir.if %[[VAL_2]] -> (!fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>) {
-! CHECK:             %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>) -> (!fir.box<!fir.array<?xnone>>, i1)
-! CHECK:             fir.result %[[VAL_4]]#0, %[[VAL_4]]#1, %[[VAL_1]]#0 : !fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:           %[[VAL_3:.*]]:4 = fir.if %[[VAL_2]] -> (!fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>) {
+! CHECK:             %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.box<!fir.array<?xnone>>>) -> (!fir.box<!fir.array<?xnone>>, i1, i1)
+! CHECK:             fir.result %[[VAL_4]]#0, %[[VAL_4]]#1, %[[VAL_4]]#2, %[[VAL_1]]#0 : !fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           } else {
 ! CHECK:             %[[VAL_6:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
 ! CHECK:             %[[VAL_7:.*]] = arith.constant false
-! CHECK:             %[[VAL_8:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
-! CHECK:             fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]] : !fir.box<!fir.array<?xnone>>, i1, !fir.box<!fir.array<?xnone>>
+! CHECK:             %[[VAL_8:.*]] = arith.constant false
+! CHECK:             %[[VAL_9:.*]] = fir.absent !fir.box<!fir.array<?xnone>>
+! CHECK:             fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !fir.box<!fir.array<?xnone>>, i1, i1, !fir.box<!fir.array<?xnone>>
 ! CHECK:           }
-! CHECK:           %[[VAL_9:.*]] = fir.convert %[[VAL_10:.*]]#0 : (!fir.box<!fir.array<?xnone>>) -> !fir.box<!fir.array<*:none>>
-! CHECK:           fir.call @_QPs5b(%[[VAL_9]]) fastmath<contract> : (!fir.box<!fir.array<*:none>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_10]]#1 to %[[VAL_10]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xnone>>>>, i1, !fir.box<!fir.array<?xnone>>) -> ()
+! CHECK:           %[[VAL_11:.*]] = fir.convert %[[VAL_12:.*]]#0 : (!fir.box<!fir.array<?xnone>>) -> !fir.box<!fir.array<*:none>>
+! CHECK:           fir.call @_QPs5b(%[[VAL_11]]) fastmath<contract> : (!fir.box<!fir.array<*:none>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_12]]#1, %[[VAL_12]]#2 to %[[VAL_12]]#3 : (!fir.ref<!fir.box<!fir.array<?xnone>>>, i1, i1, !fir.box<!fir.array<?xnone>>) -> ()
 ! CHECK:           return
 ! CHECK:         }

diff  --git a/flang/test/HLFIR/assumed_shape_with_value_keyword.f90 b/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
index 046355901e725..9d8af8306cc44 100644
--- a/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
+++ b/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
@@ -10,10 +10,10 @@ subroutine test_integer_value1(x)
 ! CHECK-LABEL:  func.func @_QPtest_integer_value1(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_integer_value1Ex"} : (!fir.box<!fir.array<?xi32>>, !fir.dscope) -> (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.box<!fir.array<?xi32>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.array<?xi32>>>) -> (!fir.box<!fir.array<?xi32>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
 ! CHECK:          fir.call @_QPinternal_call1(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, i1, !fir.box<!fir.array<?xi32>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?xi32>>>, i1, i1, !fir.box<!fir.array<?xi32>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -24,10 +24,10 @@ subroutine test_integer_value2(x)
 ! CHECK-LABEL:  func.func @_QPtest_integer_value2(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?x?xi32>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_integer_value2Ex"} : (!fir.box<!fir.array<?x?xi32>>, !fir.dscope) -> (!fir.box<!fir.array<?x?xi32>>, !fir.box<!fir.array<?x?xi32>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>) -> (!fir.box<!fir.array<?x?xi32>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xi32>>, !fir.ref<!fir.box<!fir.array<?x?xi32>>>) -> (!fir.box<!fir.array<?x?xi32>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?x?xi32>>) -> !fir.ref<!fir.array<?x?xi32>>
 ! CHECK:          fir.call @_QPinternal_call2(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?x?xi32>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>, i1, !fir.box<!fir.array<?x?xi32>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?x?xi32>>>, i1, i1, !fir.box<!fir.array<?x?xi32>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -38,10 +38,10 @@ subroutine test_real_value1(x)
 ! CHECK-LABEL:  func.func @_QPtest_real_value1(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_real_value1Ex"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
 ! CHECK:          fir.call @_QPinternal_call3(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?xf32>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -52,10 +52,10 @@ subroutine test_real_value2(x)
 ! CHECK-LABEL:  func.func @_QPtest_real_value2(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_real_value2Ex"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?x?xf32>>, !fir.box<!fir.array<?x?xf32>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>) -> (!fir.box<!fir.array<?x?xf32>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xf32>>, !fir.ref<!fir.box<!fir.array<?x?xf32>>>) -> (!fir.box<!fir.array<?x?xf32>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
 ! CHECK:          fir.call @_QPinternal_call4(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?x?xf32>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>, i1, !fir.box<!fir.array<?x?xf32>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?x?xf32>>>, i1, i1, !fir.box<!fir.array<?x?xf32>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -66,10 +66,10 @@ subroutine test_complex_value1(x)
 ! CHECK-LABEL:  func.func @_QPtest_complex_value1(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?xcomplex<f32>>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_complex_value1Ex"} : (!fir.box<!fir.array<?xcomplex<f32>>>, !fir.dscope) -> (!fir.box<!fir.array<?xcomplex<f32>>>, !fir.box<!fir.array<?xcomplex<f32>>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xcomplex<f32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xcomplex<f32>>>>>) -> (!fir.box<!fir.array<?xcomplex<f32>>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xcomplex<f32>>>, !fir.ref<!fir.box<!fir.array<?xcomplex<f32>>>>) -> (!fir.box<!fir.array<?xcomplex<f32>>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?xcomplex<f32>>>) -> !fir.ref<!fir.array<?xcomplex<f32>>>
 ! CHECK:          fir.call @_QPinternal_call5(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?xcomplex<f32>>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xcomplex<f32>>>>>, i1, !fir.box<!fir.array<?xcomplex<f32>>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?xcomplex<f32>>>>, i1, i1, !fir.box<!fir.array<?xcomplex<f32>>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -80,10 +80,10 @@ subroutine test_complex_value2(x)
 ! CHECK-LABEL:  func.func @_QPtest_complex_value2(
 ! CHECK-SAME:     %[[ARG0:.*]]: !fir.box<!fir.array<?x?xcomplex<f32>>> {fir.bindc_name = "x"}) {
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QFtest_complex_value2Ex"} : (!fir.box<!fir.array<?x?xcomplex<f32>>>, !fir.dscope) -> (!fir.box<!fir.array<?x?xcomplex<f32>>>, !fir.box<!fir.array<?x?xcomplex<f32>>>)
-! CHECK:          %[[VAL_1:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xcomplex<f32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xcomplex<f32>>>>>) -> (!fir.box<!fir.array<?x?xcomplex<f32>>>, i1)
+! CHECK:          %[[VAL_1:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xcomplex<f32>>>, !fir.ref<!fir.box<!fir.array<?x?xcomplex<f32>>>>) -> (!fir.box<!fir.array<?x?xcomplex<f32>>>, i1, i1)
 ! CHECK:          %[[VAL_2:.*]] = fir.box_addr %[[VAL_1]]#0 : (!fir.box<!fir.array<?x?xcomplex<f32>>>) -> !fir.ref<!fir.array<?x?xcomplex<f32>>>
 ! CHECK:          fir.call @_QPinternal_call6(%[[VAL_2]]) fastmath<contract> : (!fir.ref<!fir.array<?x?xcomplex<f32>>>) -> ()
-! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xcomplex<f32>>>>>, i1, !fir.box<!fir.array<?x?xcomplex<f32>>>) -> ()
+! CHECK:          hlfir.copy_out %[[TMP_BOX]], %[[VAL_1]]#1, %[[VAL_1]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?x?xcomplex<f32>>>>, i1, i1, !fir.box<!fir.array<?x?xcomplex<f32>>>) -> ()
 ! CHECK:          return
 ! CHECK:        }
 
@@ -98,10 +98,10 @@ subroutine test_optional1(x)
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional, value>, uniq_name = "_QFtest_optional1Ex"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
 ! CHECK:          %[[VAL_1:.*]] = fir.is_present %[[VAL_0]]#1 : (!fir.box<!fir.array<?xf32>>) -> i1
 ! CHECK:          fir.if %[[VAL_1:.*]] {
-! CHECK:            %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:            %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:            %[[VAL_3:.*]] = fir.box_addr %[[VAL_2]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
 ! CHECK:            fir.call @_QPinternal_call7(%[[VAL_3]]) fastmath<contract> : (!fir.ref<!fir.array<?xf32>>) -> ()
-! CHECK:            hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:            hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:          }
 ! CHECK:          return
 ! CHECK:        }
@@ -117,10 +117,10 @@ subroutine test_optional2(x)
 ! CHECK:          %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional, value>, uniq_name = "_QFtest_optional2Ex"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?x?xf32>>, !fir.box<!fir.array<?x?xf32>>)
 ! CHECK:          %[[VAL_1:.*]] = fir.is_present %[[VAL_0]]#1 : (!fir.box<!fir.array<?x?xf32>>) -> i1
 ! CHECK:          fir.if %[[VAL_1:.*]] {
-! CHECK:            %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>) -> (!fir.box<!fir.array<?x?xf32>>, i1)
+! CHECK:            %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_0]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?xf32>>, !fir.ref<!fir.box<!fir.array<?x?xf32>>>) -> (!fir.box<!fir.array<?x?xf32>>, i1, i1)
 ! CHECK:            %[[VAL_3:.*]] = fir.box_addr %[[VAL_2]]#0 : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
 ! CHECK:            fir.call @_QPinternal_call8(%[[VAL_3]]) fastmath<contract> : (!fir.ref<!fir.array<?x?xf32>>) -> ()
-! CHECK:            hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>, i1, !fir.box<!fir.array<?x?xf32>>) -> ()
+! CHECK:            hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_0]]#0 : (!fir.ref<!fir.box<!fir.array<?x?xf32>>>, i1, i1, !fir.box<!fir.array<?x?xf32>>) -> ()
 ! CHECK:          }
 ! CHECK:          return
 ! CHECK:        }

diff  --git a/flang/test/HLFIR/copy-in-out-codegen.fir b/flang/test/HLFIR/copy-in-out-codegen.fir
index f4ea36c2244e5..c73db2c342871 100644
--- a/flang/test/HLFIR/copy-in-out-codegen.fir
+++ b/flang/test/HLFIR/copy-in-out-codegen.fir
@@ -2,115 +2,91 @@
 
 // RUN: fir-opt %s -convert-hlfir-to-fir | FileCheck %s
 
-func.func @test_copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) {
-  %0:2 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+func.func @test_copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>) {
+  %0:3 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   return
 }
-// CHECK-LABEL:   func.func @test_copy_in(
-// CHECK-SAME:                            %[[VAL_0:.*]]: !fir.box<!fir.array<?xf64>>,
-// CHECK-SAME:                            %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) {
-// CHECK:           %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf64>>) -> !fir.box<none>
-// CHECK:           %[[VAL_3:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_2]]) : (!fir.box<none>) -> i1
-// CHECK:           %[[VAL_4:.*]] = fir.if %[[VAL_3]] -> (!fir.box<!fir.array<?xf64>>) {
-// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?xf64>>
-// CHECK:           } else {
-// CHECK:             %[[VAL_8:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             %[[VAL_9:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf64>>) -> !fir.box<none>
-// CHECK:             fir.call @_FortranACopyInAssign(%[[VAL_8]], %[[VAL_9]],
-// CHECK:             %[[VAL_12:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
-// CHECK:             %[[VAL_13:.*]] = fir.rebox %[[VAL_12]] : (!fir.box<!fir.heap<!fir.array<?xf64>>>) -> !fir.box<!fir.array<?xf64>>
-// CHECK:             fir.result %[[VAL_13]] : !fir.box<!fir.array<?xf64>>
-// CHECK:           }
-// CHECK:           %[[VAL_14:.*]] = arith.constant false
-// CHECK:           %[[VAL_15:.*]] = arith.cmpi eq, %[[VAL_3]], %[[VAL_14]] : i1
-// CHECK:           return
-// CHECK:         }
+// CHECK-LABEL: func.func @test_copy_in(
+// CHECK:         fir.allocmem !fir.array<?xf64>
+// CHECK:         fir.embox
+// CHECK:         fir.call @_FortranAShallowCopyDirect
+// CHECK:         fir.store %{{.*}} to %{{.*}} : !fir.ref<!fir.box<!fir.array<?xf64>>>
+// CHECK-NOT:     fir.call @_FortranACopyInAssign
+
+func.func @test_copy_in_poly_pair(%poly : !fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>, %temp: !fir.ref<!fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>>) {
+  %0:3 = hlfir.copy_in %poly to %temp : (!fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>, !fir.ref<!fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>>) -> (!fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>, i1, i1)
+  hlfir.copy_out %temp, %0#1, %0#2 : (!fir.ref<!fir.class<!fir.array<?x!fir.type<test_copy_in_poly_pairTt1{i:i32}>>>>, i1, i1) -> ()
+  return
+}
+// CHECK-LABEL: func.func @test_copy_in_poly_pair(
+// CHECK:         fir.call @_FortranACopyInAssign
+// CHECK:         fir.freemem {{.*}} : !fir.heap<i8>
 
-func.func @test_copy_in_optional(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %is_present: i1) {
-  %0:2 = hlfir.copy_in %box to %temp handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1)
+func.func @test_optional_copy_pair(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %is_present: i1) {
+  %0:3 = hlfir.copy_in %box to %temp handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
+  hlfir.copy_out %temp, %0#1, %0#2 to %box : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
   return
 }
-// CHECK-LABEL:   func.func @test_copy_in_optional(
-// CHECK-SAME:                                     %[[VAL_0:.*]]: !fir.box<!fir.array<?xf64>>,
-// CHECK-SAME:                                     %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>,
-// CHECK-SAME:                                     %[[VAL_2:.*]]: i1) {
-// CHECK:           %[[VAL_3:.*]]:2 = fir.if %[[VAL_2]] -> (!fir.box<!fir.array<?xf64>>, i1) {
-// CHECK:             %[[VAL_4:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf64>>) -> !fir.box<none>
-// CHECK:             %[[VAL_5:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_4]]) : (!fir.box<none>) -> i1
-// CHECK:             %[[VAL_6:.*]] = fir.if %[[VAL_5]] -> (!fir.box<!fir.array<?xf64>>) {
-// CHECK:               fir.result %[[VAL_0]] : !fir.box<!fir.array<?xf64>>
-// CHECK:             } else {
-// CHECK:               %[[VAL_10:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:               %[[VAL_11:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?xf64>>) -> !fir.box<none>
-// CHECK:               fir.call @_FortranACopyInAssign(%[[VAL_10]], %[[VAL_11]],
-// CHECK:               %[[VAL_14:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
-// CHECK:               %[[VAL_15:.*]] = fir.rebox %[[VAL_14]] : (!fir.box<!fir.heap<!fir.array<?xf64>>>) -> !fir.box<!fir.array<?xf64>>
-// CHECK:               fir.result %[[VAL_15]] : !fir.box<!fir.array<?xf64>>
-// CHECK:             }
-// CHECK:             %[[VAL_16:.*]] = arith.constant false
-// CHECK:             %[[VAL_17:.*]] = arith.cmpi eq, %[[VAL_5]], %[[VAL_16]] : i1
-// CHECK:             fir.result %[[VAL_6]], %[[VAL_17]] : !fir.box<!fir.array<?xf64>>, i1
+// CHECK-LABEL: func.func @test_optional_copy_pair(
+// CHECK:         fir.if %{{.*}} -> (!fir.box<!fir.array<?xf64>>, i1) {
+// CHECK:           fir.if %{{.*}} -> (!fir.box<!fir.array<?xf64>>) {
 // CHECK:           } else {
-// CHECK:             %[[VAL_18:.*]] = fir.absent !fir.box<!fir.array<?xf64>>
-// CHECK:             fir.result %[[VAL_18]], %[[VAL_2]] : !fir.box<!fir.array<?xf64>>, i1
+// CHECK:             fir.allocmem !fir.array<?xf64>
+// CHECK:             fir.embox
+// CHECK:             fir.call @_FortranAShallowCopyDirect
 // CHECK:           }
-// CHECK:           return
 // CHECK:         }
+// CHECK:         fir.if %{{.*}} {
+// CHECK:           fir.call @_FortranACopyOutAssignDirect
+// CHECK:         fir.if %{{.*}} {
+// CHECK:           fir.freemem
+// CHECK-NOT:     fir.call @_FortranACopyInAssign
+// CHECK-NOT:     fir.call @_FortranACopyOutAssign(
 
-func.func @test_copy_out_no_copy_back(%temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %was_copied: i1) {
-  hlfir.copy_out %temp, %was_copied : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
+// Assumed-rank keeps runtime allocation because its rank cannot be represented
+// by inline fir.alloca/fir.allocmem operations. Deallocation remains inline.
+func.func @test_assumed_rank_copy_pair(%box: !fir.box<!fir.array<*:f64>>, %temp: !fir.ref<!fir.box<!fir.array<*:f64>>>) {
+  %0:3 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<*:f64>>, !fir.ref<!fir.box<!fir.array<*:f64>>>) -> (!fir.box<!fir.array<*:f64>>, i1, i1)
+  hlfir.copy_out %temp, %0#1, %0#2 to %box : (!fir.ref<!fir.box<!fir.array<*:f64>>>, i1, i1, !fir.box<!fir.array<*:f64>>) -> ()
   return
 }
-// CHECK-LABEL:   func.func @test_copy_out_no_copy_back(
-// CHECK-SAME:                                          %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>,
-// CHECK-SAME:                                          %[[VAL_1:.*]]: i1) {
-// CHECK:           fir.if %[[VAL_1]] {
-// CHECK:             %[[VAL_2:.*]] = fir.zero_bits !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
-// CHECK:             %[[VAL_6:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             %[[VAL_7:.*]] = fir.convert %[[VAL_0]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             fir.call @_FortranACopyOutAssign(%[[VAL_6]], %[[VAL_7]],
-// CHECK:           }
-// CHECK:           return
-// CHECK:         }
+// CHECK-LABEL: func.func @test_assumed_rank_copy_pair(
+// CHECK:         fir.call @_FortranACopyInAssign
+// CHECK:         fir.call @_FortranACopyOutAssignDirect
+// CHECK:         fir.freemem {{.*}} : !fir.heap<i8>
 
-func.func @test_copy_out_copy_back(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %was_copied: i1) {
-  hlfir.copy_out %temp, %was_copied to %box : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+func.func @test_copy_in_optional(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %is_present: i1) {
+  %0:3 = hlfir.copy_in %box to %temp handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   return
 }
-// CHECK-LABEL:   func.func @test_copy_out_copy_back(
-// CHECK-SAME:                                       %[[VAL_0:.*]]: !fir.box<!fir.array<?xf64>>,
-// CHECK-SAME:                                       %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>,
-// CHECK-SAME:                                       %[[VAL_2:.*]]: i1) {
-// CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.array<?xf64>>
-// CHECK:           fir.if %[[VAL_2]] {
-// CHECK:             fir.store %[[VAL_0]] to %[[VAL_3]] : !fir.ref<!fir.box<!fir.array<?xf64>>>
-// CHECK:             %[[VAL_7:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.box<!fir.array<?xf64>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             %[[VAL_8:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             fir.call @_FortranACopyOutAssign(%[[VAL_7]], %[[VAL_8]],
-// CHECK:           }
-// CHECK:           return
-// CHECK:         }
+// CHECK-LABEL: func.func @test_copy_in_optional(
+// CHECK:         fir.if %{{.*}} -> (!fir.box<!fir.array<?xf64>>, i1) {
+// CHECK:           fir.call @_FortranAShallowCopyDirect
+// CHECK-NOT:     fir.call @_FortranACopyInAssign
 
-func.func @test_copy_in_poly(%poly : !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, %temp: !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>>) {
-  %0:2 = hlfir.copy_in %poly to %temp : (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>>) -> (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, i1)
+func.func @test_copy_out_no_copy_back(%temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %was_copied: i1, %must_free: i1) {
+  hlfir.copy_out %temp, %was_copied, %must_free : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
   return
 }
-// CHECK-LABEL:   func.func @test_copy_in_poly(
-// CHECK-SAME:                                 %[[VAL_0:.*]]: !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>,
-// CHECK-SAME:                                 %[[VAL_1:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>>) {
-// CHECK:           %[[VAL_2:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>) -> !fir.box<none>
-// CHECK:           %[[VAL_3:.*]] = fir.call @_FortranAIsContiguous(%[[VAL_2]]) : (!fir.box<none>) -> i1
-// CHECK:           %[[VAL_4:.*]] = fir.if %[[VAL_3]] -> (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>) {
-// CHECK:             fir.result %[[VAL_0]] : !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>
-// CHECK:           } else {
-// CHECK:             %[[VAL_8:.*]] = fir.convert %[[VAL_1]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>>) -> !fir.ref<!fir.box<none>>
-// CHECK:             %[[VAL_9:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>) -> !fir.box<none>
-// CHECK:             fir.call @_FortranACopyInAssign(%[[VAL_8]], %[[VAL_9]],
-// CHECK:             %[[VAL_12:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>>
-// CHECK:             %[[VAL_13:.*]] = fir.rebox %[[VAL_12]] : (!fir.class<!fir.heap<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>) -> !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>
-// CHECK:             fir.result %[[VAL_13]] : !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>
-// CHECK:           }
-// CHECK:           %[[VAL_14:.*]] = arith.constant false
-// CHECK:           %[[VAL_15:.*]] = arith.cmpi eq, %[[VAL_3]], %[[VAL_14]] : i1
-// CHECK:           return
-// CHECK:         }
+// CHECK-LABEL: func.func @test_copy_out_no_copy_back(
+// CHECK-NOT:     fir.call @_FortranACopyOutAssignDirect
+// CHECK:         fir.if %{{.*}} {
+// CHECK:           fir.load %{{.*}} : !fir.ref<!fir.box<!fir.array<?xf64>>>
+// CHECK:           fir.freemem
+
+func.func @test_copy_out_copy_back(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %was_copied: i1, %must_free: i1) {
+  hlfir.copy_out %temp, %was_copied, %must_free to %box : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
+  return
+}
+// CHECK-LABEL: func.func @test_copy_out_copy_back(
+// CHECK:         fir.if %{{.*}} {
+// CHECK:           fir.call @_FortranACopyOutAssignDirect({{.*}}) : (!fir.box<none>, !fir.ref<!fir.box<none>>, !fir.ref<i8>, i32) -> ()
+// CHECK:         fir.if %{{.*}} {
+// CHECK:           fir.freemem
+
+func.func @test_copy_in_poly(%poly : !fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, %temp: !fir.ref<!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>) {
+  %0:3 = hlfir.copy_in %poly to %temp : (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, !fir.ref<!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>>) -> (!fir.class<!fir.array<?x!fir.type<test_copy_in_polyTt1{i:i32}>>>, i1, i1)
+  return
+}
+// CHECK-LABEL: func.func @test_copy_in_poly(
+// CHECK:         fir.call @_FortranACopyInAssign

diff  --git a/flang/test/HLFIR/copy-in-out.fir b/flang/test/HLFIR/copy-in-out.fir
index 2db0c89c7e44b..d55ff3cf110a2 100644
--- a/flang/test/HLFIR/copy-in-out.fir
+++ b/flang/test/HLFIR/copy-in-out.fir
@@ -3,26 +3,27 @@
 
 // RUN: fir-opt %s | fir-opt | FileCheck %s
 
-func.func @test_copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %is_present: i1) {
-  %0:2 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
-  %1:2 = hlfir.copy_in %box to %temp handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1)
+func.func @test_copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %is_present: i1) {
+  %0:3 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
+  %1:3 = hlfir.copy_in %box to %temp handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   return
 }
 // CHECK-LABEL:   func.func @test_copy_in(
 // CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf64>>,
-// CHECK-SAME:    %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>,
+// CHECK-SAME:    %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.array<?xf64>>>,
 // CHECK-SAME:    %[[VAL_2:.*]]: i1
-// CHECK:  hlfir.copy_in %[[VAL_0]] to %[[VAL_1]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
-// CHECK:  hlfir.copy_in %[[VAL_0]] to %[[VAL_1]] handle_optional %[[VAL_2]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1)
+// CHECK:  hlfir.copy_in %[[VAL_0]] to %[[VAL_1]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
+// CHECK:  hlfir.copy_in %[[VAL_0]] to %[[VAL_1]] handle_optional %[[VAL_2]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
 
-func.func @test_copy_out(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %was_copied: i1) {
-  hlfir.copy_out %temp, %was_copied : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
-  hlfir.copy_out %temp, %was_copied to %box : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+func.func @test_copy_out(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %was_copied: i1, %must_free: i1) {
+  hlfir.copy_out %temp, %was_copied, %must_free : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
+  hlfir.copy_out %temp, %was_copied, %must_free to %box : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
   return
 }
 // CHECK-LABEL:   func.func @test_copy_out(
 // CHECK-SAME:    %[[VAL_0:[^:]*]]: !fir.box<!fir.array<?xf64>>,
-// CHECK-SAME:    %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>,
-// CHECK-SAME:    %[[VAL_2:.*]]: i1) {
-// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
-// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]] to %[[VAL_0]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+// CHECK-SAME:    %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.array<?xf64>>>,
+// CHECK-SAME:    %[[VAL_2:.*]]: i1,
+// CHECK-SAME:    %[[VAL_3:.*]]: i1) {
+// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
+// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] to %[[VAL_0]] : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()

diff  --git a/flang/test/HLFIR/inline-hlfir-copy-stack.fir b/flang/test/HLFIR/inline-hlfir-copy-stack.fir
index 16b5ced6d0ed1..445e064a976ab 100644
--- a/flang/test/HLFIR/inline-hlfir-copy-stack.fir
+++ b/flang/test/HLFIR/inline-hlfir-copy-stack.fir
@@ -15,34 +15,34 @@ func.func private @calleeDyn(!fir.ref<!fir.array<?xf64>>) -> ()
 
 // 8 * 8 = 64 bytes, at the threshold: the buffer goes on the stack.
 func.func @small_constant_buffer(%arg0: !fir.box<!fir.array<?x?xf64>>, %arg1: !fir.ref<i32>) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<8xf64>>>
+  %tmpBox = fir.alloca !fir.box<!fir.array<8xf64>>
   %c1 = arith.constant 1 : index
   %c8 = arith.constant 8 : index
   %1 = fir.load %arg1 : !fir.ref<i32>
   %2 = fir.convert %1 : (i32) -> i64
   %3 = fir.shape %c8 : (index) -> !fir.shape<1>
   %4 = hlfir.designate %arg0 (%2, %c1:%c8:%c1)  shape %3 : (!fir.box<!fir.array<?x?xf64>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<8xf64>>
-  %5:2 = hlfir.copy_in %4 to %0 : (!fir.box<!fir.array<8xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<8xf64>>>>) -> (!fir.box<!fir.array<8xf64>>, i1)
+  %5:3 = hlfir.copy_in %4 to %tmpBox : (!fir.box<!fir.array<8xf64>>, !fir.ref<!fir.box<!fir.array<8xf64>>>) -> (!fir.box<!fir.array<8xf64>>, i1, i1)
   %6 = fir.box_addr %5#0 : (!fir.box<!fir.array<8xf64>>) -> !fir.ref<!fir.array<8xf64>>
   fir.call @callee8(%6) : (!fir.ref<!fir.array<8xf64>>) -> ()
-  hlfir.copy_out %0, %5#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<8xf64>>>>, i1) -> ()
+  hlfir.copy_out %tmpBox, %5#1, %5#2 : (!fir.ref<!fir.box<!fir.array<8xf64>>>, i1, i1) -> ()
   return
 }
 
 // 100 * 8 = 800 bytes, above the 64 byte threshold: the buffer stays on the
 // heap and is freed by the inlined copy-out.
 func.func @big_constant_buffer(%arg0: !fir.box<!fir.array<?x?xf64>>, %arg1: !fir.ref<i32>) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<100xf64>>>
+  %tmpBox = fir.alloca !fir.box<!fir.array<100xf64>>
   %c1 = arith.constant 1 : index
   %c100 = arith.constant 100 : index
   %1 = fir.load %arg1 : !fir.ref<i32>
   %2 = fir.convert %1 : (i32) -> i64
   %3 = fir.shape %c100 : (index) -> !fir.shape<1>
   %4 = hlfir.designate %arg0 (%2, %c1:%c100:%c1)  shape %3 : (!fir.box<!fir.array<?x?xf64>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<100xf64>>
-  %5:2 = hlfir.copy_in %4 to %0 : (!fir.box<!fir.array<100xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<100xf64>>>>) -> (!fir.box<!fir.array<100xf64>>, i1)
+  %5:3 = hlfir.copy_in %4 to %tmpBox : (!fir.box<!fir.array<100xf64>>, !fir.ref<!fir.box<!fir.array<100xf64>>>) -> (!fir.box<!fir.array<100xf64>>, i1, i1)
   %6 = fir.box_addr %5#0 : (!fir.box<!fir.array<100xf64>>) -> !fir.ref<!fir.array<100xf64>>
   fir.call @callee100(%6) : (!fir.ref<!fir.array<100xf64>>) -> ()
-  hlfir.copy_out %0, %5#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<100xf64>>>>, i1) -> ()
+  hlfir.copy_out %tmpBox, %5#1, %5#2 : (!fir.ref<!fir.box<!fir.array<100xf64>>>, i1, i1) -> ()
   return
 }
 
@@ -50,16 +50,16 @@ func.func @big_constant_buffer(%arg0: !fir.box<!fir.array<?x?xf64>>, %arg1: !fir
 // because a dynamically sized fir.alloca cannot be hoisted out of the copy-in
 // branch and would grow the stack if the copy-in were inside a loop.
 func.func @dynamic_buffer(%arg0: !fir.box<!fir.array<?x?xf64>>, %arg1: !fir.ref<i32>, %n: index) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>>
+  %tmpBox = fir.alloca !fir.box<!fir.array<?xf64>>
   %c1 = arith.constant 1 : index
   %1 = fir.load %arg1 : !fir.ref<i32>
   %2 = fir.convert %1 : (i32) -> i64
   %3 = fir.shape %n : (index) -> !fir.shape<1>
   %4 = hlfir.designate %arg0 (%2, %c1:%n:%c1)  shape %3 : (!fir.box<!fir.array<?x?xf64>>, i64, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xf64>>
-  %5:2 = hlfir.copy_in %4 to %0 : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+  %5:3 = hlfir.copy_in %4 to %tmpBox : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   %6 = fir.box_addr %5#0 : (!fir.box<!fir.array<?xf64>>) -> !fir.ref<!fir.array<?xf64>>
   fir.call @calleeDyn(%6) : (!fir.ref<!fir.array<?xf64>>) -> ()
-  hlfir.copy_out %0, %5#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
+  hlfir.copy_out %tmpBox, %5#1, %5#2 : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
   return
 }
 

diff  --git a/flang/test/HLFIR/inline-hlfir-copy.fir b/flang/test/HLFIR/inline-hlfir-copy.fir
index 672fe34434c47..5fdaeebdefdac 100644
--- a/flang/test/HLFIR/inline-hlfir-copy.fir
+++ b/flang/test/HLFIR/inline-hlfir-copy.fir
@@ -3,7 +3,7 @@
 
 // Test inlining of hlfir.copy_in that does not require the array to be copied out
 func.func private @_test_inline_copy_in(%arg0: !fir.box<!fir.array<?x?x?xf64>> {fir.bindc_name = "x"}, %arg1: !fir.ref<i32> {fir.bindc_name = "i"}, %arg2: !fir.ref<i32> {fir.bindc_name = "j"}) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>>
+  %0 = fir.alloca !fir.box<!fir.array<?xf64>>
   %1 = fir.dummy_scope : !fir.dscope
   %2:2 = hlfir.declare %arg1 dummy_scope %1 {uniq_name = "_QFFsb2Ei"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
   %3:2 = hlfir.declare %arg2 dummy_scope %1 {uniq_name = "_QFFsb2Ej"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
@@ -25,11 +25,11 @@ func.func private @_test_inline_copy_in(%arg0: !fir.box<!fir.array<?x?x?xf64>> {
   %15 = fir.shape %12 : (index) -> !fir.shape<1>
   %16 = hlfir.designate %4#0 (%6, %c1:%7#1:%c1_1, %14)  shape %15 : (!fir.box<!fir.array<?x?x?xf64>>, i64, index, index, index, i64, !fir.shape<1>) -> !fir.box<!fir.array<?xf64>>
   %c100_i32 = arith.constant 100 : i32
-  %17:2 = hlfir.copy_in %16 to %0 : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+  %17:3 = hlfir.copy_in %16 to %0 : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   %18 = fir.box_addr %17#0 : (!fir.box<!fir.array<?xf64>>) -> !fir.ref<!fir.array<?xf64>>
   %19:3 = hlfir.associate %c100_i32 {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
   fir.call @_QFPsb(%18, %19#0) fastmath<contract> : (!fir.ref<!fir.array<?xf64>>, !fir.ref<i32>) -> ()
-  hlfir.copy_out %0, %17#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
+  hlfir.copy_out %0, %17#1, %17#2 : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
   hlfir.end_associate %19#1, %19#2 : !fir.ref<i32>, i1
   return
 }
@@ -86,7 +86,7 @@ func.func private @_test_inline_copy_in(%arg0: !fir.box<!fir.array<?x?x?xf64>> {
 
 // Test not inlining of hlfir.copy_in when copy-back is required (intent(inout))
 func.func private @_test_inline_copy_in_with_copyback(%arg0: !fir.box<!fir.array<?x?x?xf64>> {fir.bindc_name = "x"}, %arg1: !fir.ref<i32> {fir.bindc_name = "i"}, %arg2: !fir.ref<i32> {fir.bindc_name = "j"}) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>>
+  %0 = fir.alloca !fir.box<!fir.array<?xf64>>
   %1 = fir.dummy_scope : !fir.dscope
   %2:2 = hlfir.declare %arg1 dummy_scope %1 {uniq_name = "_QFFsb2Ei"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
   %3:2 = hlfir.declare %arg2 dummy_scope %1 {uniq_name = "_QFFsb2Ej"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
@@ -108,11 +108,11 @@ func.func private @_test_inline_copy_in_with_copyback(%arg0: !fir.box<!fir.array
   %15 = fir.shape %12 : (index) -> !fir.shape<1>
   %16 = hlfir.designate %4#0 (%6, %c1:%7#1:%c1_1, %14)  shape %15 : (!fir.box<!fir.array<?x?x?xf64>>, i64, index, index, index, i64, !fir.shape<1>) -> !fir.box<!fir.array<?xf64>>
   %c100_i32 = arith.constant 100 : i32
-  %17:2 = hlfir.copy_in %16 to %0 : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+  %17:3 = hlfir.copy_in %16 to %0 : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   %18 = fir.box_addr %17#0 : (!fir.box<!fir.array<?xf64>>) -> !fir.ref<!fir.array<?xf64>>
   %19:3 = hlfir.associate %c100_i32 {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
   fir.call @_QFPsb(%18, %19#1) fastmath<contract> : (!fir.ref<!fir.array<?xf64>>, !fir.ref<i32>) -> ()
-  hlfir.copy_out %0, %17#1 to %16 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+  hlfir.copy_out %0, %17#1, %17#2 to %16 : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
   hlfir.end_associate %19#1, %19#2 : !fir.ref<i32>, i1
   return
 }
@@ -121,71 +121,71 @@ func.func private @_test_inline_copy_in_with_copyback(%arg0: !fir.box<!fir.array
 // CHECK-SAME:      %[[VAL_0:.*]]: !fir.box<!fir.array<?x?x?xf64>> {fir.bindc_name = "x"},
 // CHECK-SAME:      %[[VAL_1:.*]]: !fir.ref<i32> {fir.bindc_name = "i"},
 // CHECK-SAME:      %[[VAL_2:.*]]: !fir.ref<i32> {fir.bindc_name = "j"}) {
-// CHECK:    %[[TMP:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>>
-// CHECK:    %[[COPYIN:.*]]:2 = hlfir.copy_in %{{.*}} to %[[TMP]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+// CHECK:    %[[TEMPBOX:.*]] = fir.alloca !fir.box<!fir.array<?xf64>>
+// CHECK:    %[[COPYIN:.*]]:3 = hlfir.copy_in %{{.*}} to %[[TEMPBOX]] : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
 // CHECK:    fir.call @_QFPsb(
-// CHECK:    hlfir.copy_out %[[TMP]], %[[COPYIN]]#1 to %{{.*}} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+// CHECK:    hlfir.copy_out %[[TEMPBOX]], %[[COPYIN]]#1, %[[COPYIN]]#2 to %{{.*}} : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
 // CHECK:    return
 // CHECK:  }
 
 // Test not inlining optional dummy arguments (no direct copy-out)
 func.func @_QPoptional_copy_in_out(%arg0: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
   %false = arith.constant false
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  %0 = fir.alloca !fir.box<!fir.array<?xf32>>
   %1 = fir.dummy_scope : !fir.dscope
   %2:2 = hlfir.declare %arg0 dummy_scope %1 {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFoptional_copy_in_outEx"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
   %3 = fir.is_present %2#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-  %4:2 = fir.if %3 -> (!fir.ref<!fir.array<?xf32>>, i1) {
-    %5:2 = hlfir.copy_in %2#0 to %0 : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+  %4:3 = fir.if %3 -> (!fir.ref<!fir.array<?xf32>>, i1, i1) {
+    %5:3 = hlfir.copy_in %2#0 to %0 : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
     %6 = fir.box_addr %5#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
-    fir.result %6, %5#1 : !fir.ref<!fir.array<?xf32>>, i1
+    fir.result %6, %5#1, %5#2 : !fir.ref<!fir.array<?xf32>>, i1, i1
   } else {
     %5 = fir.absent !fir.ref<!fir.array<?xf32>>
-    fir.result %5, %false : !fir.ref<!fir.array<?xf32>>, i1
+    fir.result %5, %false, %false : !fir.ref<!fir.array<?xf32>>, i1, i1
   }
   fir.call @_QPtakes_optional_explicit(%4#0) fastmath<contract> : (!fir.ref<!fir.array<?xf32>>) -> ()
-  hlfir.copy_out %0, %4#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1) -> ()
+  hlfir.copy_out %0, %4#1, %4#2 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1) -> ()
   return
 }
 
 // CHECK-LABEL:  func.func @_QPoptional_copy_in_out(
 // CHECK-SAME: %[[ARG_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
 // CHECK:    %false = arith.constant false
-// CHECK:    %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+// CHECK:    %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
 // CHECK:    %[[VAL_1:.*]] = fir.dummy_scope : !fir.dscope
 // CHECK:    %[[VAL_2:.*]]:2 = hlfir.declare %[[ARG_0:.*]] dummy_scope %[[VAL_1:.*]] {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFoptional_copy_in_outEx"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
 // CHECK:    %[[VAL_3:.*]] = fir.is_present %[[VAL_2:.*]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-// CHECK:    %[[VAL_4:.*]]:2 = fir.if %[[VAL_3:.*]] -> (!fir.ref<!fir.array<?xf32>>, i1) {
-// CHECK:      %[[VAL_5:.*]]:2 = hlfir.copy_in %[[VAL_2:.*]]#0 to %[[VAL_0:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+// CHECK:    %[[VAL_4:.*]]:3 = fir.if %[[VAL_3:.*]] -> (!fir.ref<!fir.array<?xf32>>, i1, i1) {
+// CHECK:      %[[VAL_5:.*]]:3 = hlfir.copy_in %[[VAL_2:.*]]#0 to %[[VAL_0:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 // CHECK:      %[[VAL_6:.*]] = fir.box_addr %[[VAL_5:.*]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
-// CHECK:      fir.result %[[VAL_6:.*]], %[[VAL_5:.*]]#1 : !fir.ref<!fir.array<?xf32>>, i1
+// CHECK:      fir.result %[[VAL_6:.*]], %[[VAL_5:.*]]#1, %[[VAL_5:.*]]#2 : !fir.ref<!fir.array<?xf32>>, i1, i1
 // CHECK:    } else {
 // CHECK:      %[[VAL_5:.*]] = fir.absent !fir.ref<!fir.array<?xf32>>
-// CHECK:      fir.result %[[VAL_5:.*]], %false : !fir.ref<!fir.array<?xf32>>, i1
+// CHECK:      fir.result %[[VAL_5:.*]], %false, %false : !fir.ref<!fir.array<?xf32>>, i1, i1
 // CHECK:    }
 // CHECK:    fir.call @_QPtakes_optional_explicit(%[[VAL_4:.*]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xf32>>) -> ()
-// CHECK:    hlfir.copy_out %[[VAL_0:.*]], %[[VAL_4:.*]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1) -> ()
+// CHECK:    hlfir.copy_out %[[VAL_0:.*]], %[[VAL_4:.*]]#1, %[[VAL_4:.*]]#2 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1) -> ()
 // CHECK:    return
 // CHECK:  }
 
 // Test not inlining of assumed-rank arrays
 func.func @_QPtest_copy_in_out_2(%arg0: !fir.box<!fir.array<*:f32>> {fir.bindc_name = "x"}) {
-  %0 = fir.alloca !fir.box<!fir.heap<!fir.array<*:f32>>>
+  %0 = fir.alloca !fir.box<!fir.array<*:f32>>
   %1 = fir.dummy_scope : !fir.dscope
   %2:2 = hlfir.declare %arg0 dummy_scope %1 {uniq_name = "_QFtest_copy_in_out_2Ex"} : (!fir.box<!fir.array<*:f32>>, !fir.dscope) -> (!fir.box<!fir.array<*:f32>>, !fir.box<!fir.array<*:f32>>)
-  %3:2 = hlfir.copy_in %2#0 to %0 : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>) -> (!fir.box<!fir.array<*:f32>>, i1)
+  %3:3 = hlfir.copy_in %2#0 to %0 : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.array<*:f32>>>) -> (!fir.box<!fir.array<*:f32>>, i1, i1)
   fir.call @_QPtakes_contiguous_intentin(%3#0) fastmath<contract> : (!fir.box<!fir.array<*:f32>>) -> ()
-  hlfir.copy_out %0, %3#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>, i1) -> ()
+  hlfir.copy_out %0, %3#1, %3#2 : (!fir.ref<!fir.box<!fir.array<*:f32>>>, i1, i1) -> ()
   return
 }
 
 // CHECK-LABEL: func.func @_QPtest_copy_in_out_2(
 // CHECK-SAME: %[[ARG_0]]: !fir.box<!fir.array<*:f32>> {fir.bindc_name = "x"}) {
-// CHECK:   %[[VAL_0]] = fir.alloca !fir.box<!fir.heap<!fir.array<*:f32>>>
+// CHECK:   %[[VAL_0]] = fir.alloca !fir.box<!fir.array<*:f32>>
 // CHECK:   %[[VAL_1]] = fir.dummy_scope : !fir.dscope
 // CHECK:   %[[VAL_2]]:2 = hlfir.declare %[[ARG_0]] dummy_scope %[[VAL_1]] {uniq_name = "_QFtest_copy_in_out_2Ex"} : (!fir.box<!fir.array<*:f32>>, !fir.dscope) -> (!fir.box<!fir.array<*:f32>>, !fir.box<!fir.array<*:f32>>)
-// CHECK:   %[[VAL_3]]:2 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>) -> (!fir.box<!fir.array<*:f32>>, i1)
+// CHECK:   %[[VAL_3]]:3 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.array<*:f32>>>) -> (!fir.box<!fir.array<*:f32>>, i1, i1)
 // CHECK:   fir.call @_QPtakes_contiguous_intentin(%[[VAL_3]]#0) fastmath<contract> : (!fir.box<!fir.array<*:f32>>) -> ()
-// CHECK:   hlfir.copy_out %[[VAL_0]], %[[VAL_3]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>, i1) -> ()
+// CHECK:   hlfir.copy_out %[[VAL_0]], %[[VAL_3]]#1, %[[VAL_3]]#2 : (!fir.ref<!fir.box<!fir.array<*:f32>>>, i1, i1) -> ()
 // CHECK:   return
 // CHECK: }

diff  --git a/flang/test/HLFIR/memory-effects.fir b/flang/test/HLFIR/memory-effects.fir
index 6c791f1260be7..094c73deecc1c 100644
--- a/flang/test/HLFIR/memory-effects.fir
+++ b/flang/test/HLFIR/memory-effects.fir
@@ -212,21 +212,21 @@ func.func @char_extremum(%arg0: !fir.ref<!fir.char<1,10>>, %arg1: !fir.ref<!fir.
   return
 }
 
-func.func @copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %is_present: i1) {
+func.func @copy_in(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %is_present: i1) {
 // expected-remark at +3 {{found an instance of 'allocate' on resource '<Default>'}}
 // expected-remark at +2 {{found an instance of 'read' on op operand 0, on resource '<Default>'}}
 // expected-remark at +1 {{found an instance of 'write' on op operand 1, on resource '<Default>'}}
-  %0:2 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+  %0:3 = hlfir.copy_in %box to %temp : (!fir.box<!fir.array<?xf64>>, !fir.ref<!fir.box<!fir.array<?xf64>>>) -> (!fir.box<!fir.array<?xf64>>, i1, i1)
   return
 }
 
-func.func @copy_out(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, %was_copied: i1) {
+func.func @copy_out(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.ref<!fir.box<!fir.array<?xf64>>>, %was_copied: i1, %must_free: i1) {
 // expected-remark at +2 {{found an instance of 'free' on resource '<Default>'}}
 // expected-remark at +1 {{found an instance of 'read' on op operand 0, on resource '<Default>'}}
-  hlfir.copy_out %temp, %was_copied : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1) -> ()
+  hlfir.copy_out %temp, %was_copied, %must_free : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1) -> ()
 // expected-remark at +3 {{found an instance of 'free' on resource '<Default>'}}
 // expected-remark at +2 {{found an instance of 'read' on op operand 0, on resource '<Default>'}}
-// expected-remark at +1 {{found an instance of 'write' on op operand 2, on resource '<Default>'}}
-  hlfir.copy_out %temp, %was_copied to %box : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+// expected-remark at +1 {{found an instance of 'write' on op operand 3, on resource '<Default>'}}
+  hlfir.copy_out %temp, %was_copied, %must_free to %box : (!fir.ref<!fir.box<!fir.array<?xf64>>>, i1, i1, !fir.box<!fir.array<?xf64>>) -> ()
   return
 }

diff  --git a/flang/test/Lower/HLFIR/assumed-rank-calls.f90 b/flang/test/Lower/HLFIR/assumed-rank-calls.f90
index 74583104c1d7e..2307d5f0a6d03 100644
--- a/flang/test/Lower/HLFIR/assumed-rank-calls.f90
+++ b/flang/test/Lower/HLFIR/assumed-rank-calls.f90
@@ -92,12 +92,12 @@ subroutine takes_contiguous(x)
 end subroutine
 ! CHECK-LABEL:   func.func @_QPtest_copy_in_out(
 ! CHECK-SAME:                                   %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>> {fir.bindc_name = "x"}) {
-! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<*:f32>>>
+! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<*:f32>>
 ! CHECK:           %[[VAL_2:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[VAL_2]] arg {{[0-9]+}} {uniq_name = "_QFtest_copy_in_outEx"} : (!fir.box<!fir.array<*:f32>>, !fir.dscope) -> (!fir.box<!fir.array<*:f32>>, !fir.box<!fir.array<*:f32>>)
-! CHECK:           %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_3]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>) -> (!fir.box<!fir.array<*:f32>>, i1)
+! CHECK:           %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_3]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.array<*:f32>>>) -> (!fir.box<!fir.array<*:f32>>, i1, i1)
 ! CHECK:           fir.call @_QPtakes_contiguous(%[[VAL_4]]#0) fastmath<contract> : (!fir.box<!fir.array<*:f32>>) -> ()
-! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1 to %[[VAL_3]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>, i1, !fir.box<!fir.array<*:f32>>) -> ()
+! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1, %[[VAL_4]]#2 to %[[VAL_3]]#0 : (!fir.ref<!fir.box<!fir.array<*:f32>>>, i1, i1, !fir.box<!fir.array<*:f32>>) -> ()
 
 subroutine test_copy_in_out_2(x)
   real ::  x(..)
@@ -110,9 +110,9 @@ subroutine takes_contiguous_intentin(x)
 end subroutine
 ! CHECK-LABEL:   func.func @_QPtest_copy_in_out_2(
 ! CHECK-SAME:                                     %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>> {fir.bindc_name = "x"}) {
-! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<*:f32>>>
+! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<*:f32>>
 ! CHECK:           %[[VAL_2:.*]] = fir.dummy_scope : !fir.dscope
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[VAL_2]] arg {{[0-9]+}} {uniq_name = "_QFtest_copy_in_out_2Ex"} : (!fir.box<!fir.array<*:f32>>, !fir.dscope) -> (!fir.box<!fir.array<*:f32>>, !fir.box<!fir.array<*:f32>>)
-! CHECK:           %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_3]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>) -> (!fir.box<!fir.array<*:f32>>, i1)
+! CHECK:           %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_3]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<*:f32>>, !fir.ref<!fir.box<!fir.array<*:f32>>>) -> (!fir.box<!fir.array<*:f32>>, i1, i1)
 ! CHECK:           fir.call @_QPtakes_contiguous_intentin(%[[VAL_4]]#0) fastmath<contract> : (!fir.box<!fir.array<*:f32>>) -> ()
-! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<*:f32>>>>, i1) -> ()
+! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1, %[[VAL_4]]#2 : (!fir.ref<!fir.box<!fir.array<*:f32>>>, i1, i1) -> ()

diff  --git a/flang/test/Lower/HLFIR/call-postponed-associate.f90 b/flang/test/Lower/HLFIR/call-postponed-associate.f90
index 18df62b44324b..6eb21f3bcb7ef 100644
--- a/flang/test/Lower/HLFIR/call-postponed-associate.f90
+++ b/flang/test/Lower/HLFIR/call-postponed-associate.f90
@@ -30,7 +30,7 @@ end function array_func2
 end subroutine test2
 ! CHECK-LABEL:   func.func @_QPtest2(
 ! CHECK:           %[[VAL_3:.*]] = arith.constant 1.000000e+00 : f32
-! CHECK:           %[[VAL_4:.*]]:2 = hlfir.copy_in %{{.*}} to %{{.*}} : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:           %[[VAL_4:.*]]:3 = hlfir.copy_in %{{.*}} to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:           %[[VAL_5:.*]] = fir.box_addr %[[VAL_4]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
 ! CHECK:           %[[VAL_6:.*]]:3 = hlfir.associate %[[VAL_3]] {adapt.valuebyref} : (f32) -> (!fir.ref<f32>, !fir.ref<f32>, i1)
 ! CHECK:           %[[VAL_17:.*]] = hlfir.eval_in_mem shape %{{.*}} : (!fir.shape<1>) -> !hlfir.expr<10xf32> {
@@ -38,7 +38,7 @@ end subroutine test2
 ! CHECK:             %[[VAL_19:.*]] = fir.call @_QParray_func2(%[[VAL_5]], %[[VAL_6]]#0) fastmath<contract> : (!fir.ref<!fir.array<?xf32>>, !fir.ref<f32>) -> !fir.array<10xf32>
 ! CHECK:             fir.save_result %[[VAL_19]] to %[[VAL_18]](%{{.*}}) : !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>, !fir.shape<1>
 ! CHECK:           }
-! CHECK:           hlfir.copy_out %{{.*}}, %[[VAL_4]]#1 to %{{.*}} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_4]]#1, %[[VAL_4]]#2 to %{{.*}} : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:           hlfir.assign %[[VAL_17]] to %{{.*}} : !hlfir.expr<10xf32>, !fir.box<!fir.array<?xf32>>
 ! CHECK:           hlfir.end_associate %[[VAL_6]]#1, %[[VAL_6]]#2 : !fir.ref<f32>, i1
 ! CHECK:           hlfir.destroy %[[VAL_17]] : !hlfir.expr<10xf32>

diff  --git a/flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90 b/flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90
index aa3c842b3fe3d..e097c7e196f71 100644
--- a/flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90
+++ b/flang/test/Lower/HLFIR/call-sequence-associated-descriptors.f90
@@ -58,7 +58,7 @@ subroutine test_char_copy_in_copy_out(x)
 ! CHECK-LABEL:   func.func @_QMbindc_seq_assocPtest_char_copy_in_copy_out(
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:.*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QMbindc_seq_assocFtest_char_copy_in_copy_outEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.box<!fir.array<?x?x!fir.char<1,?>>>)
 ! CHECK:           %[[VAL_2:.*]] = arith.constant 100 : i32
-! CHECK:           %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>>) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i1)
+! CHECK:           %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.array<?x?x!fir.char<1,?>>>>) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i1, i1)
 ! CHECK:           %[[VAL_4:.*]] = arith.constant 0 : index
 ! CHECK:           %[[VAL_5:.*]] = fir.shift %[[VAL_4]], %[[VAL_4]] : (index, index) -> !fir.shift<2>
 ! CHECK:           %[[VAL_6:.*]] = fir.rebox %[[VAL_3]]#0(%[[VAL_5]]) : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.shift<2>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
@@ -81,7 +81,7 @@ subroutine test_char_copy_in_copy_out(x)
 ! CHECK:           %[[VAL_23:.*]] = fir.convert %[[VAL_22]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x!fir.char<1,?>>>
 ! CHECK:           %[[VAL_24:.*]] = fir.embox %[[VAL_23]](%[[VAL_20]]) typeparams %[[VAL_21]] : (!fir.ref<!fir.array<?x!fir.char<1,?>>>, !fir.shapeshift<1>, index) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
 ! CHECK:           fir.call @takes_char(%[[VAL_24]], %[[VAL_7]]#0) proc_attrs<bind_c> fastmath<contract> : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.ref<i32>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>>, i1, !fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.array<?x?x!fir.char<1,?>>>>, i1, i1, !fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> ()
 ! CHECK:           hlfir.end_associate %[[VAL_7]]#1, %[[VAL_7]]#2 : !fir.ref<i32>, i1
 ! CHECK:           return
 ! CHECK:         }
@@ -92,7 +92,7 @@ subroutine test_char_assumed_size(x)
   end subroutine
 ! CHECK-LABEL:   func.func @_QMbindc_seq_assocPtest_char_assumed_size(
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:.*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QMbindc_seq_assocFtest_char_assumed_sizeEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.box<!fir.array<?x?x!fir.char<1,?>>>)
-! CHECK:           %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>>) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i1)
+! CHECK:           %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.array<?x?x!fir.char<1,?>>>>) -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i1, i1)
 ! CHECK:           %[[VAL_3:.*]] = arith.constant 0 : index
 ! CHECK:           %[[VAL_4:.*]] = fir.shift %[[VAL_3]], %[[VAL_3]] : (index, index) -> !fir.shift<2>
 ! CHECK:           %[[VAL_5:.*]] = fir.rebox %[[VAL_2]]#0(%[[VAL_4]]) : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.shift<2>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
@@ -114,7 +114,7 @@ subroutine test_char_assumed_size(x)
 ! CHECK:           %[[VAL_21:.*]] = fir.convert %[[VAL_20]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<10x?x!fir.char<1,?>>>
 ! CHECK:           %[[VAL_22:.*]] = fir.embox %[[VAL_21]](%[[VAL_18]]) typeparams %[[VAL_19]] : (!fir.ref<!fir.array<10x?x!fir.char<1,?>>>, !fir.shapeshift<2>, index) -> !fir.box<!fir.array<10x?x!fir.char<1,?>>>
 ! CHECK:           fir.call @takes_char_assumed_size(%[[VAL_22]]) proc_attrs<bind_c> fastmath<contract> : (!fir.box<!fir.array<10x?x!fir.char<1,?>>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>>, i1, !fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.box<!fir.array<?x?x!fir.char<1,?>>>>, i1, i1, !fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 
@@ -216,7 +216,7 @@ subroutine test_poly_copy_in_copy_out(x)
 ! CHECK-LABEL:   func.func @_QMpoly_seq_assocPtest_poly_copy_in_copy_out(
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:.*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QMpoly_seq_assocFtest_poly_copy_in_copy_outEx"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> (!fir.class<!fir.array<?x?xnone>>, !fir.class<!fir.array<?x?xnone>>)
 ! CHECK:           %[[VAL_2:.*]] = arith.constant 100 : i32
-! CHECK:           %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1)
+! CHECK:           %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.array<?x?xnone>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1, i1)
 ! CHECK:           %[[VAL_4:.*]]:3 = hlfir.associate %[[VAL_2]] {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
 ! CHECK:           %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_4]]#0 {uniq_name = "_QMpoly_seq_assocFtakes_polyEn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
 ! CHECK:           %[[VAL_6:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i32>
@@ -234,7 +234,7 @@ subroutine test_poly_copy_in_copy_out(x)
 ! CHECK:           %[[VAL_18:.*]] = fir.convert %[[VAL_17]] : (!fir.ref<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<?xnone>>
 ! CHECK:           %[[VAL_19:.*]] = fir.embox %[[VAL_18]](%[[VAL_16]]) source_box %[[VAL_3]]#0 : (!fir.ref<!fir.array<?xnone>>, !fir.shape<1>, !fir.class<!fir.array<?x?xnone>>) -> !fir.class<!fir.array<?xnone>>
 ! CHECK:           fir.call @_QPtakes_poly(%[[VAL_19]], %[[VAL_4]]#0) fastmath<contract> : (!fir.class<!fir.array<?xnone>>, !fir.ref<i32>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>, i1, !fir.class<!fir.array<?x?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.array<?x?xnone>>>, i1, i1, !fir.class<!fir.array<?x?xnone>>) -> ()
 ! CHECK:           hlfir.end_associate %[[VAL_4]]#1, %[[VAL_4]]#2 : !fir.ref<i32>, i1
 ! CHECK:           return
 ! CHECK:         }
@@ -245,7 +245,7 @@ subroutine test_poly_assumed_size(x)
   end subroutine
 ! CHECK-LABEL:   func.func @_QMpoly_seq_assocPtest_poly_assumed_size(
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:.*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QMpoly_seq_assocFtest_poly_assumed_sizeEx"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> (!fir.class<!fir.array<?x?xnone>>, !fir.class<!fir.array<?x?xnone>>)
-! CHECK:           %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1)
+! CHECK:           %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.array<?x?xnone>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1, i1)
 ! CHECK:           %[[VAL_3:.*]] = arith.constant 10 : i64
 ! CHECK:           %[[VAL_4:.*]] = arith.constant 1 : i64
 ! CHECK:           %[[VAL_5:.*]] = arith.subi %[[VAL_3]], %[[VAL_4]] : i64
@@ -262,7 +262,7 @@ subroutine test_poly_assumed_size(x)
 ! CHECK:           %[[VAL_16:.*]] = fir.convert %[[VAL_15]] : (!fir.ref<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<10x?xnone>>
 ! CHECK:           %[[VAL_17:.*]] = fir.embox %[[VAL_16]](%[[VAL_14]]) source_box %[[VAL_2]]#0 : (!fir.ref<!fir.array<10x?xnone>>, !fir.shape<2>, !fir.class<!fir.array<?x?xnone>>) -> !fir.class<!fir.array<10x?xnone>>
 ! CHECK:           fir.call @_QPtakes_poly_assumed_size(%[[VAL_17]]) fastmath<contract> : (!fir.class<!fir.array<10x?xnone>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>, i1, !fir.class<!fir.array<?x?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.array<?x?xnone>>>, i1, i1, !fir.class<!fir.array<?x?xnone>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 

diff  --git a/flang/test/Lower/HLFIR/calls-assumed-shape.f90 b/flang/test/Lower/HLFIR/calls-assumed-shape.f90
index 9bf150d805d99..bd9408e48e040 100644
--- a/flang/test/Lower/HLFIR/calls-assumed-shape.f90
+++ b/flang/test/Lower/HLFIR/calls-assumed-shape.f90
@@ -42,10 +42,10 @@ subroutine takes_contiguous_assumed(x)
 ! CHECK-LABEL: func.func @_QPtest_ptr_to_contiguous_assumed(
 ! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:[a-z0-9]*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtest_ptr_to_contiguous_assumedEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>)
 ! CHECK:  %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-! CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_2]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+! CHECK:  %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_2]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
 ! CHECK:  %[[VAL_4:.*]] = fir.rebox %[[VAL_3]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.box<!fir.array<?xf32>>
 ! CHECK:  fir.call @_QPtakes_contiguous_assumed(%[[VAL_4]]) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1 to %[[VAL_2]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_2]] : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 
 subroutine test_ptr_to_contiguous_assumed_classstar(p)
   interface
@@ -59,10 +59,10 @@ subroutine takes_contiguous_assumed_classstar(x)
 ! CHECK-LABEL: func.func @_QPtest_ptr_to_contiguous_assumed_classstar(
 ! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:[a-z0-9]*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtest_ptr_to_contiguous_assumed_classstarEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>)
 ! CHECK:  %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-! CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_2]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+! CHECK:  %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_2]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
 ! CHECK:  %[[VAL_4:.*]] = fir.rebox %[[VAL_3]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.class<!fir.array<?xnone>>
 ! CHECK:  fir.call @_QPtakes_contiguous_assumed_classstar(%[[VAL_4]]) {{.*}} : (!fir.class<!fir.array<?xnone>>) -> ()
-! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1 to %[[VAL_2]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_2]] : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 
 subroutine test_ptr_to_assumed_typestar(p)
   interface

diff  --git a/flang/test/Lower/HLFIR/calls-constant-expr-arg.f90 b/flang/test/Lower/HLFIR/calls-constant-expr-arg.f90
index 2186bda79a46f..7e992bea91c8b 100644
--- a/flang/test/Lower/HLFIR/calls-constant-expr-arg.f90
+++ b/flang/test/Lower/HLFIR/calls-constant-expr-arg.f90
@@ -37,10 +37,10 @@ end subroutine sub
 ! CHECK:           %[[VAL_18:.*]] = arith.select %[[VAL_17]], %[[VAL_16]], %[[VAL_13]] : index
 ! CHECK:           %[[VAL_19:.*]] = fir.shape %[[VAL_18]] : (index) -> !fir.shape<1>
 ! CHECK:           %[[VAL_20:.*]] = hlfir.designate %[[VAL_10]]#0 (%[[VAL_11]]:%[[VAL_8]]:%[[VAL_12]])  shape %[[VAL_19]] : (!fir.box<!fir.array<?xi32>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
-! CHECK:           %[[VAL_21:.*]]:2 = hlfir.copy_in %[[VAL_20]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.box<!fir.array<?xi32>>, i1)
+! CHECK:           %[[VAL_21:.*]]:3 = hlfir.copy_in %[[VAL_20]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.array<?xi32>>>) -> (!fir.box<!fir.array<?xi32>>, i1, i1)
 ! CHECK:           %[[VAL_22:.*]] = fir.box_addr %[[VAL_21]]#0 : (!fir.box<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
 ! CHECK:           fir.call @_QPsub2(%[[VAL_22]]) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_21]]#1 to %[[VAL_20]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, i1, !fir.box<!fir.array<?xi32>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_21]]#1, %[[VAL_21]]#2 to %[[VAL_20]] : (!fir.ref<!fir.box<!fir.array<?xi32>>>, i1, i1, !fir.box<!fir.array<?xi32>>) -> ()
 ! CHECK:           return
 ! CHECK:         }
 

diff  --git a/flang/test/Lower/HLFIR/calls-optional.f90 b/flang/test/Lower/HLFIR/calls-optional.f90
index d8d3bfe4f4e5f..8906a2584459a 100644
--- a/flang/test/Lower/HLFIR/calls-optional.f90
+++ b/flang/test/Lower/HLFIR/calls-optional.f90
@@ -16,18 +16,19 @@ subroutine takes_optional_explicit(x)
 ! CHECK-LABEL: func.func @_QPoptional_copy_in_out(
 ! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:[a-z0-9]*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFoptional_copy_in_outEx"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
 ! CHECK:  %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-! CHECK:  %[[VAL_3:.*]]:3 = fir.if %[[VAL_2]] -> (!fir.ref<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>) {
-! CHECK:    %[[VAL_4:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:  %[[VAL_3:.*]]:4 = fir.if %[[VAL_2]] -> (!fir.ref<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>) {
+! CHECK:    %[[VAL_4:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:    %[[VAL_5:.*]] = fir.box_addr %[[VAL_4]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
-! CHECK:    fir.result %[[VAL_5]], %[[VAL_4]]#1, %[[VAL_1]]#0 : !fir.ref<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>
+! CHECK:    fir.result %[[VAL_5]], %[[VAL_4]]#1, %[[VAL_4]]#2, %[[VAL_1]]#0 : !fir.ref<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
 ! CHECK:  } else {
 ! CHECK:    %[[VAL_7:.*]] = fir.absent !fir.ref<!fir.array<?xf32>>
 ! CHECK:    %[[VAL_8:.*]] = arith.constant false
-! CHECK:    %[[VAL_9:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
-! CHECK:    fir.result %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !fir.ref<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>
+! CHECK:    %[[VAL_9:.*]] = arith.constant false
+! CHECK:    %[[VAL_10:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
+! CHECK:    fir.result %[[VAL_7]], %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !fir.ref<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
 ! CHECK:  }
 ! CHECK:  fir.call @_QPtakes_optional_explicit(%[[VAL_3]]#0) {{.*}} : (!fir.ref<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1 to %[[VAL_3]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:  hlfir.copy_out %[[TMP_BOX]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_3]]#3 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 
 subroutine optional_value_copy(x)
   interface

diff  --git a/flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90 b/flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90
index 24d7ca9aee494..bbf5ef55dc8a7 100644
--- a/flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90
+++ b/flang/test/Lower/HLFIR/calls-poly-to-assumed-type.f90
@@ -13,8 +13,8 @@ subroutine assumed_type_assumed_size(x)
 end subroutine
 ! CHECK-LABEL:   func.func @_QPpass_poly_to_assumed_type_assumed_size(
 ! CHECK:           %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0:[a-z0-9]*]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFpass_poly_to_assumed_type_assumed_sizeEx"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> (!fir.class<!fir.array<?x?xnone>>, !fir.class<!fir.array<?x?xnone>>)
-! CHECK:           %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1)
+! CHECK:           %[[VAL_2:.*]]:3 = hlfir.copy_in %[[VAL_1]]#0 to %[[TMP_BOX:.*]] : (!fir.class<!fir.array<?x?xnone>>, !fir.ref<!fir.class<!fir.array<?x?xnone>>>) -> (!fir.class<!fir.array<?x?xnone>>, i1, i1)
 ! CHECK:           %[[VAL_3:.*]] = fir.box_addr %[[VAL_2]]#0 : (!fir.class<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<?x?xnone>>
 ! CHECK:           %[[VAL_4:.*]] = fir.convert %[[VAL_3]] : (!fir.ref<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<?xnone>>
 ! CHECK:           fir.call @_QPassumed_type_assumed_size(%[[VAL_4]]) fastmath<contract> : (!fir.ref<!fir.array<?xnone>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>, i1, !fir.class<!fir.array<?x?xnone>>) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_2]]#1, %[[VAL_2]]#2 to %[[VAL_1]]#0 : (!fir.ref<!fir.class<!fir.array<?x?xnone>>>, i1, i1, !fir.class<!fir.array<?x?xnone>>) -> ()

diff  --git a/flang/test/Lower/HLFIR/calls-poly-to-nonpoly.f90 b/flang/test/Lower/HLFIR/calls-poly-to-nonpoly.f90
index 3c60a84692bdb..120050f497611 100644
--- a/flang/test/Lower/HLFIR/calls-poly-to-nonpoly.f90
+++ b/flang/test/Lower/HLFIR/calls-poly-to-nonpoly.f90
@@ -16,11 +16,11 @@ subroutine sequence_assoc(x, n)
 end subroutine
 ! CHECK-LABEL:   func.func @_QPtest_sequence_association(
 ! CHECK-SAME:                                            %[[VAL_0:.*]]: !fir.class<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>
-! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>>
+! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]]
 ! CHECK:           %[[REBOX:.*]] = fir.rebox %[[VAL_3]]#0 : (!fir.class<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>) -> !fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>
-! CHECK:           %[[VAL_5:.*]]:2 = hlfir.copy_in %[[REBOX]] to %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>>>) -> (!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>, i1)
+! CHECK:           %[[VAL_5:.*]]:3 = hlfir.copy_in %[[REBOX]] to %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>, !fir.ref<!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>>) -> (!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>, i1, i1)
 ! CHECK:           %[[VAL_6:.*]] = fir.box_addr %[[VAL_5]]#0 : (!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>) -> !fir.ref<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>
 ! CHECK:           %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (!fir.ref<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>) -> !fir.ref<!fir.array<?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>
 ! CHECK:           fir.call @_QPsequence_assoc(%[[VAL_7]], %{{.*}})
-! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_5]]#1 to %[[REBOX]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>>>, i1, !fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>) -> ()
+! CHECK:           hlfir.copy_out %[[VAL_1]], %[[VAL_5]]#1, %[[VAL_5]]#2 to %[[REBOX]] : (!fir.ref<!fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>>, i1, i1, !fir.box<!fir.array<?x?x!fir.type<_QFtest_sequence_associationTt{i:i32}>>>) -> ()

diff  --git a/flang/test/Lower/HLFIR/poly_expr_for_nonpoly_dummy.f90 b/flang/test/Lower/HLFIR/poly_expr_for_nonpoly_dummy.f90
index 822a0bcc20e53..26599acc5b7ae 100644
--- a/flang/test/Lower/HLFIR/poly_expr_for_nonpoly_dummy.f90
+++ b/flang/test/Lower/HLFIR/poly_expr_for_nonpoly_dummy.f90
@@ -22,9 +22,9 @@ end subroutine test1
 ! CHECK:           %[[VAL_26:.*]] = hlfir.shape_of %[[VAL_23]] : (!hlfir.expr<?x!fir.type<_QMtypesTt>?>) -> !fir.shape<1>
 ! CHECK:           %[[VAL_27:.*]]:3 = hlfir.associate %[[VAL_23]](%[[VAL_26]]) {adapt.valuebyref} : (!hlfir.expr<?x!fir.type<_QMtypesTt>?>, !fir.shape<1>) -> (!fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, i1)
 ! CHECK:           %[[VAL_28:.*]] = fir.rebox %[[VAL_27]]#0 : (!fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>) -> !fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>
-! CHECK:           %[[VAL_29:.*]]:2 = hlfir.copy_in %[[VAL_28]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QMtypesTt>>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, i1)
+! CHECK:           %[[VAL_29:.*]]:3 = hlfir.copy_in %[[VAL_28]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.ref<!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, i1, i1)
 ! CHECK:           fir.call @_QMtypesPcallee(%[[VAL_29]]#0) fastmath<contract> : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_29]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QMtypesTt>>>>>, i1) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_29]]#1, %[[VAL_29]]#2 : (!fir.ref<!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>>, i1, i1) -> ()
 ! CHECK:           hlfir.end_associate %[[VAL_27]]#0, %[[VAL_27]]#2 : !fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, i1
 ! CHECK:           hlfir.destroy %[[VAL_23]] : !hlfir.expr<?x!fir.type<_QMtypesTt>?>
 
@@ -37,8 +37,8 @@ end subroutine test2
 ! CHECK:           %[[VAL_5:.*]] = hlfir.elemental %{{.*}} mold %{{.*}} unordered : (!fir.shape<1>, !fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>) -> !hlfir.expr<?x!fir.type<_QMtypesTt>?> {
 ! CHECK:           %[[VAL_9:.*]]:3 = hlfir.associate %[[VAL_5]](%{{.*}}) {adapt.valuebyref} : (!hlfir.expr<?x!fir.type<_QMtypesTt>?>, !fir.shape<1>) -> (!fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, i1)
 ! CHECK:           %[[VAL_10:.*]] = fir.rebox %[[VAL_9]]#0 : (!fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>) -> !fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>
-! CHECK:           %[[VAL_11:.*]]:2 = hlfir.copy_in %[[VAL_10]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QMtypesTt>>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, i1)
+! CHECK:           %[[VAL_11:.*]]:3 = hlfir.copy_in %[[VAL_10]] to %[[TMP_BOX:.*]] : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, !fir.ref<!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>, i1, i1)
 ! CHECK:           fir.call @_QMtypesPcallee(%[[VAL_11]]#0) fastmath<contract> : (!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>) -> ()
-! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_11]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QMtypesTt>>>>>, i1) -> ()
+! CHECK:           hlfir.copy_out %[[TMP_BOX]], %[[VAL_11]]#1, %[[VAL_11]]#2 : (!fir.ref<!fir.box<!fir.array<?x!fir.type<_QMtypesTt>>>>, i1, i1) -> ()
 ! CHECK:           hlfir.end_associate %[[VAL_9]]#0, %[[VAL_9]]#2 : !fir.class<!fir.array<?x!fir.type<_QMtypesTt>>>, i1
 ! CHECK:           hlfir.destroy %[[VAL_5]] : !hlfir.expr<?x!fir.type<_QMtypesTt>?>

diff  --git a/flang/test/Lower/OpenMP/copyin.f90 b/flang/test/Lower/OpenMP/copyin.f90
index 6cdbbd2c12e6e..46fb3c8dc9b0d 100644
--- a/flang/test/Lower/OpenMP/copyin.f90
+++ b/flang/test/Lower/OpenMP/copyin.f90
@@ -349,18 +349,18 @@ subroutine common_2()
 ! CHECK:           %[[VAL_2:.*]] = omp.threadprivate %[[VAL_1]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFpointerEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>)
 ! CHECK:           omp.parallel {
-! CHECK:             %[[VAL_4:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>> {pinned}
+! CHECK:             %[[VAL_4:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xi32>>> {pinned}
 ! CHECK:             %[[VAL_5:.*]] = omp.threadprivate %[[VAL_1]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
 ! CHECK:             %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFpointerEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>)
 ! CHECK:             %[[VAL_7:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
 ! CHECK:             fir.store %[[VAL_7]] to %[[VAL_6]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
 ! CHECK:             omp.barrier
 ! CHECK:             %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
-! CHECK:             %[[VAL_9:.*]]:2 = hlfir.copy_in %[[VAL_8]] to %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i1)
+! CHECK:             %[[VAL_9:.*]]:3 = hlfir.copy_in %[[VAL_8]] to %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xi32>>>, i1, i1)
 ! CHECK:             %[[VAL_10:.*]] = fir.box_addr %[[VAL_9]]#0 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> !fir.ptr<!fir.array<?xi32>>
 ! CHECK:             %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.ptr<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
 ! CHECK:             fir.call @_QPsub7(%[[VAL_11]]) fastmath<contract> : (!fir.ref<!fir.array<?xi32>>) -> ()
-! CHECK:             hlfir.copy_out %[[VAL_4]], %[[VAL_9]]#1 to %[[VAL_8]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xi32>>>) -> ()
+! CHECK:             hlfir.copy_out %[[VAL_4]], %[[VAL_9]]#1, %[[VAL_9]]#2 to %[[VAL_8]] : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xi32>>>) -> ()
 ! CHECK:             omp.terminator
 ! CHECK:           }
 ! CHECK:           return

diff  --git a/flang/test/Lower/call-copy-in-out.f90 b/flang/test/Lower/call-copy-in-out.f90
index 3acda2ddda17c..06d6af28ee55e 100644
--- a/flang/test/Lower/call-copy-in-out.f90
+++ b/flang/test/Lower/call-copy-in-out.f90
@@ -7,10 +7,10 @@
 subroutine test_assumed_shape_to_array(x)
   real :: x(:)
 ! CHECK: %[[x_decl:.*]]:2 = hlfir.declare %[[x]]
-! CHECK: %[[copy_in:.*]]:2 = hlfir.copy_in %[[x_decl]]#0 {{.*}}
+! CHECK: %[[copy_in:.*]]:3 = hlfir.copy_in %[[x_decl]]#0 {{.*}}
 ! CHECK: %[[addr:.*]] = fir.box_addr %[[copy_in]]#0
 ! CHECK: fir.call @_QPbar(%[[addr]])
-! CHECK: hlfir.copy_out %{{.*}}, %[[copy_in]]#1 to %[[x_decl]]#0
+! CHECK: hlfir.copy_out %{{.*}}, %[[copy_in]]#1, %[[copy_in]]#2 to %[[x_decl]]#0
   call bar(x)
 end subroutine
 
@@ -80,7 +80,7 @@ subroutine bar_intent_in(x)
 ! CHECK: hlfir.copy_in
 ! CHECK: fir.call @_QPbar_intent_in
 ! CHECK: hlfir.copy_out
-! CHECK-SAME: %{{.*}}, %{{.*}} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1) -> ()
+! CHECK-SAME: %{{.*}}, %{{.*}}, %{{.*}} : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1) -> ()
   call bar_intent_in(x)
 end subroutine
 

diff  --git a/flang/test/Lower/components.f90 b/flang/test/Lower/components.f90
index c1aed155a343c..0ca464bc4d102 100644
--- a/flang/test/Lower/components.f90
+++ b/flang/test/Lower/components.f90
@@ -221,10 +221,10 @@ subroutine extended_type_components
   type, extends(t3) :: t4
     integer :: t4i
   end type t4
-! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<5xi32>>>
-! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<5xi32>>>
-! CHECK:           %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<5xi32>>>
-! CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<5xi32>>>
+! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.array<5xi32>>
+! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<5xi32>>
+! CHECK:           %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.array<5xi32>>
+! CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.array<5xi32>>
 ! CHECK:           %[[VAL_4:.*]] = fir.alloca !fir.type<_QFextended_type_componentsTu3
 ! CHECK:           %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_4]] {uniq_name = "_QFextended_type_componentsEu3v"} : (!fir.ref<!fir.type<_QFextended_type_componentsTu3
 ! CHECK:           %[[VAL_6:.*]] = arith.constant 5 : index
@@ -284,7 +284,7 @@ subroutine extended_type_components
 ! CHECK:           %[[VAL_31:.*]] = hlfir.designate %[[VAL_30]]{"t2"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt3
 ! CHECK:           %[[VAL_32:.*]] = hlfir.designate %[[VAL_31]]{"t1"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt2
 ! CHECK:           %[[VAL_33:.*]] = hlfir.designate %[[VAL_32]]{"t1i"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt1
-! CHECK:           %[[VAL_34:.*]]:2 = hlfir.copy_in %[[VAL_33]] to %[[VAL_3]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<5xi32>>>>) -> (!fir.box<!fir.array<5xi32>>, i1)
+! CHECK:           %[[VAL_34:.*]]:3 = hlfir.copy_in %[[VAL_33]] to %[[VAL_3]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.array<5xi32>>>) -> (!fir.box<!fir.array<5xi32>>, i1, i1)
 ! CHECK:           %[[VAL_35:.*]] = fir.box_addr %[[VAL_34]]#0 : (!fir.box<!fir.array<5xi32>>) -> !fir.ref<!fir.array<5xi32>>
 ! CHECK:           fir.call @_QPfoo5(%[[VAL_35]]) fastmath<contract> : (!fir.ref<!fir.array<5xi32>>) -> ()
 
@@ -294,7 +294,7 @@ subroutine extended_type_components
 ! CHECK:           %[[VAL_38:.*]] = hlfir.designate %[[VAL_37]]{"t2"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt3
 ! CHECK:           %[[VAL_39:.*]] = hlfir.designate %[[VAL_38]]{"t1"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt2
 ! CHECK:           %[[VAL_40:.*]] = hlfir.designate %[[VAL_39]]{"t1i"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt1
-! CHECK:           %[[VAL_41:.*]]:2 = hlfir.copy_in %[[VAL_40]] to %[[VAL_2]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<5xi32>>>>) -> (!fir.box<!fir.array<5xi32>>, i1)
+! CHECK:           %[[VAL_41:.*]]:3 = hlfir.copy_in %[[VAL_40]] to %[[VAL_2]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.array<5xi32>>>) -> (!fir.box<!fir.array<5xi32>>, i1, i1)
 ! CHECK:           %[[VAL_42:.*]] = fir.box_addr %[[VAL_41]]#0 : (!fir.box<!fir.array<5xi32>>) -> !fir.ref<!fir.array<5xi32>>
 ! CHECK:           fir.call @_QPfoo6(%[[VAL_42]]) fastmath<contract> : (!fir.ref<!fir.array<5xi32>>) -> ()
 
@@ -304,7 +304,7 @@ subroutine extended_type_components
 ! CHECK:           %[[VAL_45:.*]] = hlfir.designate %[[VAL_44]]{"t2"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt3
 ! CHECK:           %[[VAL_46:.*]] = hlfir.designate %[[VAL_45]]{"t1"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt2
 ! CHECK:           %[[VAL_47:.*]] = hlfir.designate %[[VAL_46]]{"t1i"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt1
-! CHECK:           %[[VAL_48:.*]]:2 = hlfir.copy_in %[[VAL_47]] to %[[VAL_1]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<5xi32>>>>) -> (!fir.box<!fir.array<5xi32>>, i1)
+! CHECK:           %[[VAL_48:.*]]:3 = hlfir.copy_in %[[VAL_47]] to %[[VAL_1]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.array<5xi32>>>) -> (!fir.box<!fir.array<5xi32>>, i1, i1)
 ! CHECK:           %[[VAL_49:.*]] = fir.box_addr %[[VAL_48]]#0 : (!fir.box<!fir.array<5xi32>>) -> !fir.ref<!fir.array<5xi32>>
 ! CHECK:           fir.call @_QPfoo7(%[[VAL_49]]) fastmath<contract> : (!fir.ref<!fir.array<5xi32>>) -> ()
 
@@ -313,7 +313,7 @@ subroutine extended_type_components
 ! CHECK:           %[[VAL_51:.*]] = hlfir.designate %[[VAL_50]]{"u2t4"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTu2
 ! CHECK:           %[[VAL_52:.*]] = hlfir.designate %[[VAL_51]]{"t2"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt3
 ! CHECK:           %[[VAL_53:.*]] = hlfir.designate %[[VAL_52]]{"t2i"}   shape %[[VAL_8]] : (!fir.box<!fir.array<5x!fir.type<_QFextended_type_componentsTt2
-! CHECK:           %[[VAL_54:.*]]:2 = hlfir.copy_in %[[VAL_53]] to %[[VAL_0]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<5xi32>>>>) -> (!fir.box<!fir.array<5xi32>>, i1)
+! CHECK:           %[[VAL_54:.*]]:3 = hlfir.copy_in %[[VAL_53]] to %[[VAL_0]] : (!fir.box<!fir.array<5xi32>>, !fir.ref<!fir.box<!fir.array<5xi32>>>) -> (!fir.box<!fir.array<5xi32>>, i1, i1)
 ! CHECK:           %[[VAL_55:.*]] = fir.box_addr %[[VAL_54]]#0 : (!fir.box<!fir.array<5xi32>>) -> !fir.ref<!fir.array<5xi32>>
 ! CHECK:           fir.call @_QPfoo8(%[[VAL_55]]) fastmath<contract> : (!fir.ref<!fir.array<5xi32>>) -> ()
 end subroutine extended_type_components

diff  --git a/flang/test/Lower/dummy-argument-assumed-shape-optional.f90 b/flang/test/Lower/dummy-argument-assumed-shape-optional.f90
index a1fcea20c3eaf..e9e729c026e4f 100644
--- a/flang/test/Lower/dummy-argument-assumed-shape-optional.f90
+++ b/flang/test/Lower/dummy-argument-assumed-shape-optional.f90
@@ -22,11 +22,11 @@ subroutine test_assumed_shape_to_contiguous(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_assumed_shape_to_contiguous(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x"}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_assumed_shape_to_contiguousEx"{{.*}}
-! CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:  %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:  fir.call @_QPtakes_contiguous(%[[VAL_3]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 
@@ -50,11 +50,11 @@ subroutine test_assumed_shape_opt_to_contiguous(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_assumed_shape_opt_to_contiguous(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_assumed_shape_opt_to_contiguousEx"{{.*}}
-! CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:  %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:  fir.call @_QPtakes_contiguous(%[[VAL_3]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 
@@ -81,11 +81,11 @@ subroutine test_assumed_shape_to_contiguous_opt(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_assumed_shape_to_contiguous_opt(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x"}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_assumed_shape_to_contiguous_optEx"{{.*}}
-! CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+! CHECK:  %[[VAL_3:.*]]:3 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
 ! CHECK:  fir.call @_QPtakes_contiguous_optional(%[[VAL_3]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_3]]#1, %[[VAL_3]]#2 to %[[VAL_2]]#0 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 
@@ -109,20 +109,21 @@ subroutine test_assumed_shape_opt_to_contiguous_opt(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_assumed_shape_opt_to_contiguous_opt(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_assumed_shape_opt_to_contiguous_optEx"{{.*}}
 ! CHECK:  %[[VAL_3:.*]] = fir.is_present %[[VAL_2]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-! CHECK:  %[[VAL_4:.*]]:3 = fir.if %[[VAL_3]] -> (!fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>) {
-! CHECK:    %[[VAL_5:.*]]:2 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
-! CHECK:    fir.result %[[VAL_5]]#0, %[[VAL_5]]#1, %[[VAL_2]]#0 : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>
+! CHECK:  %[[VAL_4:.*]]:4 = fir.if %[[VAL_3]] -> (!fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>) {
+! CHECK:    %[[VAL_5:.*]]:3 = hlfir.copy_in %[[VAL_2]]#0 to %[[VAL_1]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
+! CHECK:    fir.result %[[VAL_5]]#0, %[[VAL_5]]#1, %[[VAL_5]]#2, %[[VAL_2]]#0 : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
 ! CHECK:  } else {
 ! CHECK:    %[[VAL_6:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
 ! CHECK:    %[[VAL_7:.*]] = arith.constant false
-! CHECK:    %[[VAL_8:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
-! CHECK:    fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]] : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.array<?xf32>>
+! CHECK:    %[[VAL_8:.*]] = arith.constant false
+! CHECK:    %[[VAL_9:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
+! CHECK:    fir.result %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
 ! CHECK:  }
 ! CHECK:  fir.call @_QPtakes_contiguous_optional(%[[VAL_4]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1 to %[[VAL_4]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_4]]#1, %[[VAL_4]]#2 to %[[VAL_4]]#3 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 
@@ -157,25 +158,26 @@ subroutine test_pointer_to_contiguous_opt(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_pointer_to_contiguous_opt(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> {fir.bindc_name = "x"}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_pointer_to_contiguous_optEx"{{.*}}
 ! CHECK:  %[[VAL_3:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
 ! CHECK:  %[[VAL_4:.*]] = fir.box_addr %[[VAL_3]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_5:.*]] = fir.convert %[[VAL_4]] : (!fir.ptr<!fir.array<?xf32>>) -> i64
 ! CHECK:  %[[VAL_6:.*]] = arith.cmpi ne, %[[VAL_5]], %c0_i64 : i64
-! CHECK:  %[[VAL_7:.*]]:3 = fir.if %[[VAL_6]] -> (!fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
+! CHECK:  %[[VAL_7:.*]]:4 = fir.if %[[VAL_6]] -> (!fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
 ! CHECK:    %[[VAL_8:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-! CHECK:    %[[VAL_9:.*]]:2 = hlfir.copy_in %[[VAL_8]] to %[[VAL_1]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+! CHECK:    %[[VAL_9:.*]]:3 = hlfir.copy_in %[[VAL_8]] to %[[VAL_1]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
 ! CHECK:    %[[VAL_10:.*]] = fir.rebox %[[VAL_9]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.box<!fir.array<?xf32>>
-! CHECK:    fir.result %[[VAL_10]], %[[VAL_9]]#1, %[[VAL_8]] : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    fir.result %[[VAL_10]], %[[VAL_9]]#1, %[[VAL_9]]#2, %[[VAL_8]] : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  } else {
 ! CHECK:    %[[VAL_11:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
 ! CHECK:    %[[VAL_12:.*]] = arith.constant false
-! CHECK:    %[[VAL_13:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
-! CHECK:    fir.result %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    %[[VAL_13:.*]] = arith.constant false
+! CHECK:    %[[VAL_14:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    fir.result %[[VAL_11]], %[[VAL_12]], %[[VAL_13]], %[[VAL_14]] : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  }
 ! CHECK:  fir.call @_QPtakes_contiguous_optional(%[[VAL_7]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_7]]#1 to %[[VAL_7]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_7]]#1, %[[VAL_7]]#2 to %[[VAL_7]]#3 : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 
@@ -208,25 +210,26 @@ subroutine test_pointer_opt_to_contiguous_opt(x)
 end subroutine
 ! CHECK-LABEL: func.func @_QMtestsPtest_pointer_opt_to_contiguous_opt(
 ! CHECK-SAME:    %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> {fir.bindc_name = "x", fir.optional}) {
-! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK:  %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMtestsFtest_pointer_opt_to_contiguous_optEx"{{.*}}
 ! CHECK:  %[[VAL_3:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
 ! CHECK:  %[[VAL_4:.*]] = fir.box_addr %[[VAL_3]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
 ! CHECK:  %[[VAL_5:.*]] = fir.convert %[[VAL_4]] : (!fir.ptr<!fir.array<?xf32>>) -> i64
 ! CHECK:  %[[VAL_6:.*]] = arith.cmpi ne, %[[VAL_5]], %c0_i64 : i64
-! CHECK:  %[[VAL_7:.*]]:3 = fir.if %[[VAL_6]] -> (!fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
+! CHECK:  %[[VAL_7:.*]]:4 = fir.if %[[VAL_6]] -> (!fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
 ! CHECK:    %[[VAL_8:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-! CHECK:    %[[VAL_9:.*]]:2 = hlfir.copy_in %[[VAL_8]] to %[[VAL_1]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+! CHECK:    %[[VAL_9:.*]]:3 = hlfir.copy_in %[[VAL_8]] to %[[VAL_1]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
 ! CHECK:    %[[VAL_10:.*]] = fir.rebox %[[VAL_9]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.box<!fir.array<?xf32>>
-! CHECK:    fir.result %[[VAL_10]], %[[VAL_9]]#1, %[[VAL_8]] : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    fir.result %[[VAL_10]], %[[VAL_9]]#1, %[[VAL_9]]#2, %[[VAL_8]] : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  } else {
 ! CHECK:    %[[VAL_11:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
 ! CHECK:    %[[VAL_12:.*]] = arith.constant false
-! CHECK:    %[[VAL_13:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
-! CHECK:    fir.result %[[VAL_11]], %[[VAL_12]], %[[VAL_13]] : !fir.box<!fir.array<?xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    %[[VAL_13:.*]] = arith.constant false
+! CHECK:    %[[VAL_14:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
+! CHECK:    fir.result %[[VAL_11]], %[[VAL_12]], %[[VAL_13]], %[[VAL_14]] : !fir.box<!fir.array<?xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
 ! CHECK:  }
 ! CHECK:  fir.call @_QPtakes_contiguous_optional(%[[VAL_7]]#0) {{.*}} : (!fir.box<!fir.array<?xf32>>) -> ()
-! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_7]]#1 to %[[VAL_7]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+! CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_7]]#1, %[[VAL_7]]#2 to %[[VAL_7]]#3 : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 ! CHECK:  return
 ! CHECK:}
 

diff  --git a/flang/test/Lower/dummy-argument-optional-2.f90 b/flang/test/Lower/dummy-argument-optional-2.f90
index da666708b203b..fe672a247f575 100644
--- a/flang/test/Lower/dummy-argument-optional-2.f90
+++ b/flang/test/Lower/dummy-argument-optional-2.f90
@@ -137,56 +137,58 @@ subroutine pass_allocatable_scalar_char(c)
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>{{.*}}) {
 subroutine pass_pointer_array(i)
   real, pointer :: i(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_pointer_arrayEi"{{.*}}
   call takes_opt_explicit_shape(i)
   ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
   ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
   ! CHECK: %[[ADDR_I64:.*]] = fir.convert %[[ADDR]] : (!fir.ptr<!fir.array<?xf32>>) -> i64
   ! CHECK: %[[IS_PRESENT:.*]] = arith.cmpi ne, %[[ADDR_I64]], %c0_i64 : i64
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
   ! CHECK:   %[[LOAD2:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ptr<!fir.array<?xf32>>) -> !fir.ref<!fir.array<100xf32>>
-  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[LOAD2]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[LOAD2]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.ref<!fir.array<100xf32>>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape(%[[ARG]]#0) {{.*}} : (!fir.ref<!fir.array<100xf32>>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 end subroutine
 
 ! CHECK-LABEL: func @_QMoptional_testsPpass_pointer_array_char(
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>>{{.*}}) {
 subroutine pass_pointer_array_char(c)
   character(:), pointer :: c(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_pointer_array_charEc"{{.*}}
   call takes_opt_explicit_shape_char(c)
   ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>>
   ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) -> !fir.ptr<!fir.array<?x!fir.char<1,?>>>
   ! CHECK: %[[ADDR_I64:.*]] = fir.convert %[[ADDR]] : (!fir.ptr<!fir.array<?x!fir.char<1,?>>>) -> i64
   ! CHECK: %[[IS_PRESENT:.*]] = arith.cmpi ne, %[[ADDR_I64]], %c0_i64 : i64
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.boxchar<1>, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) {
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.boxchar<1>, i1, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) {
   ! CHECK:   %[[LOAD2:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>>
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>, i1)
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) -> !fir.ptr<!fir.array<?x!fir.char<1,?>>>
   ! CHECK:   %[[ELE_SIZE:.*]] = fir.box_elesize %[[COPY_IN]]#0 : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) -> index
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ptr<!fir.array<?x!fir.char<1,?>>>) -> !fir.ref<!fir.char<1,?>>
   ! CHECK:   %[[EMBOX:.*]] = fir.emboxchar %[[REF]], %[[ELE_SIZE]] : (!fir.ref<!fir.char<1,?>>, index) -> !fir.boxchar<1>
-  ! CHECK:   fir.result %[[EMBOX]], %[[COPY_IN]]#1, %[[LOAD2]] : !fir.boxchar<1>, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
+  ! CHECK:   fir.result %[[EMBOX]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[LOAD2]] : !fir.boxchar<1>, i1, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.boxchar<1>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.boxchar<1>, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.boxchar<1>, i1, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape_char(%[[ARG]]#0) {{.*}} : (!fir.boxchar<1>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,?>>>>) -> ()
 end subroutine
 
 ! This case is bit special because the pointer is not a symbol but a function
@@ -194,7 +196,7 @@ subroutine pass_pointer_array_char(c)
 
 ! CHECK-LABEL: func @_QMoptional_testsPforward_pointer_array() {
 subroutine forward_pointer_array()
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: %[[RES:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>> {bindc_name = ".result"}
   call takes_opt_explicit_shape(returns_pointer())
   ! CHECK: %[[RET:.*]] = fir.call @_QPreturns_pointer() {{.*}} : () -> !fir.box<!fir.ptr<!fir.array<?xf32>>>
@@ -204,20 +206,21 @@ subroutine forward_pointer_array()
   ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
   ! CHECK: %[[ADDR_I64:.*]] = fir.convert %[[ADDR]] : (!fir.ptr<!fir.array<?xf32>>) -> i64
   ! CHECK: %[[IS_PRESENT:.*]] = arith.cmpi ne, %[[ADDR_I64]], %c0_i64 : i64
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) {
   ! CHECK:   %[[LOAD2:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1)
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[LOAD2]] to %[[ALLOCA]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.box<!fir.ptr<!fir.array<?xf32>>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ptr<!fir.array<?xf32>>) -> !fir.ref<!fir.array<100xf32>>
-  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[LOAD2]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[LOAD2]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.ref<!fir.array<100xf32>>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?xf32>>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape(%[[ARG]]#0) {{.*}} : (!fir.ref<!fir.array<100xf32>>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, i1, i1, !fir.box<!fir.ptr<!fir.array<?xf32>>>) -> ()
 end subroutine
 
 ! -----------------------------------------------------------------------------
@@ -231,48 +234,50 @@ subroutine forward_pointer_array()
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
 subroutine pass_opt_assumed_shape(x)
   real, optional :: x(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_opt_assumed_shapeEx"{{.*}}
   call takes_opt_explicit_shape(x)
   ! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>) {
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>) {
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ref<!fir.array<?xf32>>) -> !fir.ref<!fir.array<100xf32>>
-  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[DECL]]#0 : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>
+  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[DECL]]#0 : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.ref<!fir.array<100xf32>>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape(%[[ARG]]#0) {{.*}} : (!fir.ref<!fir.array<100xf32>>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 end subroutine
 
 ! CHECK-LABEL: func @_QMoptional_testsPpass_opt_assumed_shape_char(
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "c", fir.optional}) {
 subroutine pass_opt_assumed_shape_char(c)
   character(*), optional :: c(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.array<?x!fir.char<1,?>>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_opt_assumed_shape_charEc"{{.*}}
   call takes_opt_explicit_shape_char(c)
   ! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL]]#0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> i1
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.boxchar<1>, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>) {
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, i1)
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.boxchar<1>, i1, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>) {
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.ref<!fir.box<!fir.array<?x!fir.char<1,?>>>>) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x!fir.char<1,?>>>
   ! CHECK:   %[[ELE_SIZE:.*]] = fir.box_elesize %[[COPY_IN]]#0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ref<!fir.array<?x!fir.char<1,?>>>) -> !fir.ref<!fir.char<1,?>>
   ! CHECK:   %[[EMBOX:.*]] = fir.emboxchar %[[REF]], %[[ELE_SIZE]] : (!fir.ref<!fir.char<1,?>>, index) -> !fir.boxchar<1>
-  ! CHECK:   fir.result %[[EMBOX]], %[[COPY_IN]]#1, %[[DECL]]#0 : !fir.boxchar<1>, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>
+  ! CHECK:   fir.result %[[EMBOX]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[DECL]]#0 : !fir.boxchar<1>, i1, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.boxchar<1>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.array<?x!fir.char<1,?>>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.boxchar<1>, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.boxchar<1>, i1, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape_char(%[[ARG]]#0) {{.*}} : (!fir.boxchar<1>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.array<?x!fir.char<1,?>>>>, i1, i1, !fir.box<!fir.array<?x!fir.char<1,?>>>) -> ()
 end subroutine
 
 ! -----------------------------------------------------------------------------
@@ -429,44 +434,46 @@ subroutine pass_contiguous_pointer_array_char(c)
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
 subroutine pass_opt_assumed_shape_to_intentin(x)
   real, optional :: x(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_opt_assumed_shape_to_intentinEx"{{.*}}
   call takes_opt_explicit_shape_intentin(x)
   ! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-  ! CHECK: %[[ARG:.*]]:2 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1) {
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, i1) {
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ref<!fir.array<?xf32>>) -> !fir.ref<!fir.array<100xf32>>
-  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1 : !fir.ref<!fir.array<100xf32>>, i1
+  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[COPY_IN]]#2 : !fir.ref<!fir.array<100xf32>>, i1, i1
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.ref<!fir.array<100xf32>>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]] : !fir.ref<!fir.array<100xf32>>, i1
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]] : !fir.ref<!fir.array<100xf32>>, i1, i1
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape_intentin(%[[ARG]]#0) {{.*}} : (!fir.ref<!fir.array<100xf32>>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1) -> ()
 end subroutine
 
 ! CHECK-LABEL: func @_QMoptional_testsPpass_opt_assumed_shape_to_intentout(
 ! CHECK-SAME:  %[[VAL_0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
 subroutine pass_opt_assumed_shape_to_intentout(x)
   real, optional :: x(:)
-  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
+  ! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
   ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {{.*}}uniq_name = "_QMoptional_testsFpass_opt_assumed_shape_to_intentoutEx"{{.*}}
   call takes_opt_explicit_shape_intentout(x)
   ! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL]]#0 : (!fir.box<!fir.array<?xf32>>) -> i1
-  ! CHECK: %[[ARG:.*]]:3 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>) {
-  ! CHECK:   %[[COPY_IN:.*]]:2 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.box<!fir.array<?xf32>>, i1)
+  ! CHECK: %[[ARG:.*]]:4 = fir.if %[[IS_PRESENT]] -> (!fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>) {
+  ! CHECK:   %[[COPY_IN:.*]]:3 = hlfir.copy_in %[[DECL]]#0 to %[[ALLOCA]] : (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.box<!fir.array<?xf32>>>) -> (!fir.box<!fir.array<?xf32>>, i1, i1)
   ! CHECK:   %[[BOX_ADDR:.*]] = fir.box_addr %[[COPY_IN]]#0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
   ! CHECK:   %[[REF:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.ref<!fir.array<?xf32>>) -> !fir.ref<!fir.array<100xf32>>
-  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[DECL]]#0 : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>
+  ! CHECK:   fir.result %[[REF]], %[[COPY_IN]]#1, %[[COPY_IN]]#2, %[[DECL]]#0 : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
   ! CHECK: } else {
   ! CHECK:   %[[ABSENT:.*]] = fir.absent !fir.ref<!fir.array<100xf32>>
   ! CHECK:   %[[FALSE:.*]] = arith.constant false
+  ! CHECK:   %[[FALSE2:.*]] = arith.constant false
   ! CHECK:   %[[ABSENT_BOX:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
-  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, !fir.box<!fir.array<?xf32>>
+  ! CHECK:   fir.result %[[ABSENT]], %[[FALSE]], %[[FALSE2]], %[[ABSENT_BOX]] : !fir.ref<!fir.array<100xf32>>, i1, i1, !fir.box<!fir.array<?xf32>>
   ! CHECK: }
   ! CHECK: fir.call @_QPtakes_opt_explicit_shape_intentout(%[[ARG]]#0) {{.*}} : (!fir.ref<!fir.array<100xf32>>) -> ()
-  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1 to %[[ARG]]#2 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i1, !fir.box<!fir.array<?xf32>>) -> ()
+  ! CHECK: hlfir.copy_out %[[ALLOCA]], %[[ARG]]#1, %[[ARG]]#2 to %[[ARG]]#3 : (!fir.ref<!fir.box<!fir.array<?xf32>>>, i1, i1, !fir.box<!fir.array<?xf32>>) -> ()
 end subroutine
 end module

diff  --git a/flang/test/Lower/parent-component.f90 b/flang/test/Lower/parent-component.f90
index 66d25a0c12c86..f1b52618ac703 100644
--- a/flang/test/Lower/parent-component.f90
+++ b/flang/test/Lower/parent-component.f90
@@ -43,7 +43,7 @@ subroutine init_with_slice()
     print*,y(:)%p
   end subroutine
   ! CHECK-LABEL: func.func private @_QFPinit_with_slice()
-  ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>
+  ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           %[[VAL_1:.*]] = fir.address_of(@_QFFinit_with_sliceEy) : !fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>
   ! CHECK:           %[[VAL_2:.*]] = arith.constant 2 : index
   ! CHECK:           %[[VAL_3:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
@@ -54,10 +54,10 @@ subroutine init_with_slice()
   ! CHECK:           %[[VAL_8:.*]] = fir.shape %[[VAL_7]] : (index) -> !fir.shape<1>
   ! CHECK:           %[[VAL_9:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_5]]:%[[VAL_2]]:%[[VAL_6]])  shape %[[VAL_8]] : (!fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>
   ! CHECK:           %[[VAL_10:.*]] = hlfir.designate %[[VAL_9]]{"p"}   shape %[[VAL_8]] : (!fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
-  ! CHECK:           %[[VAL_11:.*]]:2 = hlfir.copy_in %[[VAL_10]] to %[[VAL_0]] : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>>) -> (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, i1)
+  ! CHECK:           %[[VAL_11:.*]]:3 = hlfir.copy_in %[[VAL_10]] to %[[VAL_0]] : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>) -> (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, i1, i1)
   ! CHECK:           %[[VAL_12:.*]] = fir.box_addr %[[VAL_11]]#0 : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>) -> !fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           fir.call @_QFPprint_p(%[[VAL_12]]) fastmath<contract> : (!fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>) -> ()
-  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_11]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>>, i1) -> ()
+  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_11]]#1, %[[VAL_11]]#2 : (!fir.ref<!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>, i1, i1) -> ()
 
   subroutine init_no_slice()
     type(c) :: y(2) = [ c(11, 21), c(12, 22) ]
@@ -65,16 +65,16 @@ subroutine init_no_slice()
     print*,y%p
   end subroutine
   ! CHECK-LABEL: func.func private @_QFPinit_no_slice()
-  ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>
+  ! CHECK:           %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           %[[VAL_1:.*]] = fir.address_of(@_QFFinit_no_sliceEy) : !fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>
   ! CHECK:           %[[VAL_2:.*]] = arith.constant 2 : index
   ! CHECK:           %[[VAL_3:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
   ! CHECK:           %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]](%[[VAL_3]]) {uniq_name = "_QFFinit_no_sliceEy"} : (!fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>, !fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>)
   ! CHECK:           %[[VAL_5:.*]] = hlfir.designate %[[VAL_4]]#0{"p"}   shape %[[VAL_3]] : (!fir.ref<!fir.array<2x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
-  ! CHECK:           %[[VAL_6:.*]]:2 = hlfir.copy_in %[[VAL_5]] to %[[VAL_0]] : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>>) -> (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, i1)
+  ! CHECK:           %[[VAL_6:.*]]:3 = hlfir.copy_in %[[VAL_5]] to %[[VAL_0]] : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>) -> (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>, i1, i1)
   ! CHECK:           %[[VAL_7:.*]] = fir.box_addr %[[VAL_6]]#0 : (!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>) -> !fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           fir.call @_QFPprint_p(%[[VAL_7]]) fastmath<contract> : (!fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>) -> ()
-  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_6]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>>, i1) -> ()
+  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_6]]#1, %[[VAL_6]]#2 : (!fir.ref<!fir.box<!fir.array<2x!fir.type<_QFTp{a:i32}>>>>, i1, i1) -> ()
 
   subroutine init_allocatable()
     type(c), allocatable :: y(:)
@@ -94,11 +94,11 @@ subroutine init_allocatable()
   ! CHECK:           %[[VAL_32:.*]]:3 = fir.box_dims %[[VAL_30]], %[[VAL_31]] : (!fir.box<!fir.heap<!fir.array<?x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>>, index) -> (index, index, index)
   ! CHECK:           %[[VAL_33:.*]] = fir.shape %[[VAL_32]]#1 : (index) -> !fir.shape<1>
   ! CHECK:           %[[VAL_34:.*]] = hlfir.designate %[[VAL_30]]{"p"}   shape %[[VAL_33]] : (!fir.box<!fir.heap<!fir.array<?x!fir.type<_QFTc{p:!fir.type<_QFTp{a:i32}>,b:i32}>>>>, !fir.shape<1>) -> !fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>
-  ! CHECK:           %[[VAL_35:.*]]:2 = hlfir.copy_in %[[VAL_34]] to %[[VAL_0:.*]] : (!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QFTp{a:i32}>>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>, i1)
+  ! CHECK:           %[[VAL_35:.*]]:3 = hlfir.copy_in %[[VAL_34]] to %[[VAL_0:.*]] : (!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>, !fir.ref<!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>>) -> (!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>, i1, i1)
   ! CHECK:           %[[VAL_36:.*]] = fir.box_addr %[[VAL_35]]#0 : (!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>) -> !fir.ref<!fir.array<?x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           %[[VAL_37:.*]] = fir.convert %[[VAL_36]] : (!fir.ref<!fir.array<?x!fir.type<_QFTp{a:i32}>>>) -> !fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>
   ! CHECK:           fir.call @_QFPprint_p(%[[VAL_37]]) fastmath<contract> : (!fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>>) -> ()
-  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_35]]#1 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.type<_QFTp{a:i32}>>>>>, i1) -> ()
+  ! CHECK:           hlfir.copy_out %[[VAL_0]], %[[VAL_35]]#1, %[[VAL_35]]#2 : (!fir.ref<!fir.box<!fir.array<?x!fir.type<_QFTp{a:i32}>>>>, i1, i1) -> ()
 
   subroutine init_scalar()
     type(c) :: s = c(11, 21)


        


More information about the flang-commits mailing list