[flang-commits] [flang] Reland "[Flang]Add support for inlining hlfir.assign operation where both LHS and RHS are slices of the same array #204532" (PR #208159)
Kaviya Rajendiran via flang-commits
flang-commits at lists.llvm.org
Wed Jul 8 00:26:17 PDT 2026
https://github.com/kaviya2510 updated https://github.com/llvm/llvm-project/pull/208159
>From dbdabb1ec0a4e7fdb02e4715dbe04ce9b1fa2b8a Mon Sep 17 00:00:00 2001
From: Kaviya Rajendiran <kaviyara2000 at gmail.com>
Date: Wed, 8 Jul 2026 12:00:35 +0530
Subject: [PATCH] Reland "[Flang]Add support for inlining hlfir.assign
operation where both LHS and RHS are slices of the same array #204532"
---
.../Optimizer/Analysis/ArraySectionAnalyzer.h | 2 +-
.../flang/Optimizer/Builder/FIRBuilder.h | 10 +
flang/lib/Optimizer/Builder/FIRBuilder.cpp | 137 +++++++++++++
.../HLFIR/Transforms/InlineHLFIRAssign.cpp | 78 +++++--
.../inline-hlfir-assign-pointer-overlap.fir | 140 +++++++++++++
.../inline-hlfir-assign-scalar-index.fir | 106 ++++++++++
...-hlfir-assign-self-copy-runtime-stride.fir | 149 ++++++++++++++
.../HLFIR/inline-hlfir-assign-self-copy.fir | 190 ++++++++++++++++++
...line-hlfir-assign-forall-threadprivate.f90 | 119 +++++++++++
.../Lower/inline-hlfir-assign-pointer-omp.f90 | 125 ++++++++++++
10 files changed, 1042 insertions(+), 14 deletions(-)
create mode 100644 flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir
create mode 100644 flang/test/HLFIR/inline-hlfir-assign-scalar-index.fir
create mode 100644 flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
create mode 100644 flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
create mode 100644 flang/test/Lower/inline-hlfir-assign-forall-threadprivate.f90
create mode 100644 flang/test/Lower/inline-hlfir-assign-pointer-omp.f90
diff --git a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
index e87e37c3c5590..aae9cbde2d7be 100644
--- a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
+++ b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
@@ -65,7 +65,6 @@ class ArraySectionAnalyzer {
static bool isDesignatingArrayInOrder(hlfir::DesignateOp designate,
hlfir::ElementalOpInterface elemental);
-private:
struct SectionDesc {
// An array section is described by <lb, ub, stride> tuple.
// If the designator's subscript is not a triple, then
@@ -103,6 +102,7 @@ class ArraySectionAnalyzer {
static std::pair<mlir::Value, mlir::Value>
getOrderedBounds(const SectionDesc &desc);
+private:
// Given two array sections <lb1, ub1, stride1> and
// <lb2, ub2, stride2>, return true only if the sections
// are known to be disjoint.
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 3622351c1ef64..f88c19049f42d 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -990,6 +990,16 @@ mlir::Value getDescriptorWithNewBaseAddress(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value box,
mlir::Value newAddr);
+/// Generate a index-based disjointness check.
+std::optional<mlir::Value>
+genIndexBasedDisjointnessCheck(mlir::Location loc, fir::FirOpBuilder &builder,
+ mlir::Value lhsRef, mlir::Value rhsRef);
+
+/// Generate a address-based disjointness check.
+std::optional<mlir::Value>
+genAddressBasedDisjointnessCheck(mlir::Location loc, fir::FirOpBuilder &builder,
+ mlir::Value lhsRef, mlir::Value rhsRef);
+
} // namespace fir::factory
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index d97b56af570b4..33360d891c3fa 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -8,6 +8,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Analysis/AliasAnalysis.h"
+#include "flang/Optimizer/Analysis/ArraySectionAnalyzer.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/Complex.h"
@@ -21,10 +22,12 @@
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/Support/DataLayout.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
@@ -2007,3 +2010,137 @@ mlir::Value fir::factory::getDescriptorWithNewBaseAddress(
return builder.createBox(loc, boxType, newAddr, shape, /*slice=*/{},
fir::getTypeParams(openedInput), typeMold);
}
+
+std::optional<mlir::Value> fir::factory::genIndexBasedDisjointnessCheck(
+ mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value lhsRef,
+ mlir::Value rhsRef) {
+ auto des1 = lhsRef.getDefiningOp<hlfir::DesignateOp>();
+ auto des2 = rhsRef.getDefiningOp<hlfir::DesignateOp>();
+ if (!des1 || !des2)
+ return std::nullopt;
+
+ if (des1.getMemref() != des2.getMemref())
+ return std::nullopt;
+
+ if (des1.getComponent() != des2.getComponent() ||
+ des1.getComponentShape() != des2.getComponentShape() ||
+ des1.getSubstring() != des2.getSubstring() ||
+ des1.getComplexPart() != des2.getComplexPart() ||
+ des1.getTypeparams() != des2.getTypeparams())
+ return std::nullopt;
+
+ if (des1.getIsTriplet().empty() ||
+ !llvm::equal(des1.getIsTriplet(), des2.getIsTriplet()))
+ return std::nullopt;
+
+ using Analyzer = fir::ArraySectionAnalyzer;
+ mlir::Type idxTy = builder.getIndexType();
+ auto toIdx = [&](mlir::Value v) -> mlir::Value {
+ return fir::ConvertOp::create(builder, loc, idxTy, v);
+ };
+
+ mlir::Value disjoint;
+ auto des1It = des1.getIndices().begin();
+ auto des2It = des2.getIndices().begin();
+ for (bool isTriplet : des1.getIsTriplet()) {
+ Analyzer::SectionDesc desc1 = Analyzer::readSectionDesc(des1It, isTriplet);
+ Analyzer::SectionDesc desc2 = Analyzer::readSectionDesc(des2It, isTriplet);
+ auto [lb1, ub1] = Analyzer::getOrderedBounds(desc1);
+ auto [lb2, ub2] = Analyzer::getOrderedBounds(desc2);
+ if (!lb1 || !lb2)
+ continue;
+
+ mlir::Value c1 = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::slt, toIdx(ub1), toIdx(lb2));
+ mlir::Value c2 = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::slt, toIdx(ub2), toIdx(lb1));
+ mlir::Value c1Orc2 = mlir::arith::OrIOp::create(builder, loc, c1, c2);
+ disjoint = disjoint
+ ? mlir::arith::OrIOp::create(builder, loc, disjoint, c1Orc2)
+ : c1Orc2;
+ }
+ if (!disjoint)
+ return std::nullopt;
+ return disjoint;
+}
+
+std::optional<mlir::Value> fir::factory::genAddressBasedDisjointnessCheck(
+ mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value lhsRef,
+ mlir::Value rhsRef) {
+ if (!mlir::isa<fir::BaseBoxType>(lhsRef.getType()) ||
+ !mlir::isa<fir::BaseBoxType>(rhsRef.getType()))
+ return std::nullopt;
+
+ mlir::Type idxTy = builder.getIndexType();
+ mlir::Type intPtrTy = builder.getIntPtrType();
+
+ // Disjoint if: xEnd < yStart || yEnd < xStart.
+ auto computeRange =
+ [&](mlir::Value box) -> std::pair<mlir::Value, mlir::Value> {
+ mlir::Value baseAddr = fir::BoxAddrOp::create(builder, loc, box);
+ mlir::Value baseInt =
+ fir::ConvertOp::create(builder, loc, intPtrTy, baseAddr);
+
+ mlir::Value eleSize = fir::BoxEleSizeOp::create(builder, loc, idxTy, box);
+ mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
+ mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
+
+ mlir::Value least = zero;
+ mlir::Value most = zero;
+
+ auto boxTy = mlir::cast<fir::BaseBoxType>(box.getType());
+ unsigned rank = 0;
+ if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(
+ fir::unwrapRefType(boxTy.getEleTy())))
+ rank = seqTy.getShape().size();
+
+ if (rank == 0)
+ return {nullptr, nullptr};
+
+ for (unsigned dim = 0; dim < rank; ++dim) {
+ mlir::Value dimVal = builder.createIntegerConstant(loc, idxTy, dim);
+ auto dims = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, box,
+ dimVal);
+ mlir::Value extent = dims.getExtent();
+ mlir::Value stride = dims.getByteStride();
+
+ mlir::Value extentM1 =
+ mlir::arith::SubIOp::create(builder, loc, extent, one);
+ mlir::Value dimOffset =
+ mlir::arith::MulIOp::create(builder, loc, extentM1, stride);
+
+ mlir::Value isStrideNeg = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::slt, stride, zero);
+ mlir::Value addToLeast = mlir::arith::SelectOp::create(
+ builder, loc, isStrideNeg, dimOffset, zero);
+ mlir::Value addToMost = mlir::arith::SelectOp::create(
+ builder, loc, isStrideNeg, zero, dimOffset);
+ least = mlir::arith::AddIOp::create(builder, loc, least, addToLeast);
+ most = mlir::arith::AddIOp::create(builder, loc, most, addToMost);
+ }
+
+ mlir::Value eleSizeM1 =
+ mlir::arith::SubIOp::create(builder, loc, eleSize, one);
+ most = mlir::arith::AddIOp::create(builder, loc, most, eleSizeM1);
+
+ mlir::Value leastInt =
+ fir::ConvertOp::create(builder, loc, intPtrTy, least);
+ mlir::Value mostInt = fir::ConvertOp::create(builder, loc, intPtrTy, most);
+ mlir::Value rangeStart =
+ mlir::arith::AddIOp::create(builder, loc, baseInt, leastInt);
+ mlir::Value rangeEnd =
+ mlir::arith::AddIOp::create(builder, loc, baseInt, mostInt);
+ return {rangeStart, rangeEnd};
+ };
+
+ auto [lhsStart, lhsEnd] = computeRange(lhsRef);
+ auto [rhsStart, rhsEnd] = computeRange(rhsRef);
+ if (!lhsStart || !rhsStart)
+ return std::nullopt;
+
+ mlir::Value cond1 = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::ult, lhsEnd, rhsStart);
+ mlir::Value cond2 = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::ult, rhsEnd, lhsStart);
+ return mlir::arith::OrIOp::create(builder, loc, cond1, cond2);
+}
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
index 281c6f4d14ed0..44affd514f613 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
@@ -102,11 +102,9 @@ class InlineHLFIRAssignConversion
return rewriter.notifyMatchFailure(assign,
"RHS/LHS element types mismatch");
- if (rhs.isArray() && !mlir::isa<hlfir::ExprType>(rhs.getType())) {
- // If RHS is not an hlfir.expr, then we should prove that
- // LHS and RHS do not alias.
- // TODO: if they may alias, we can insert hlfir.as_expr for RHS,
- // and proceed with the inlining.
+ bool rhsNeedsTemporary = false;
+ if (rhs.isArray() && !mlir::isa<hlfir::ExprType>(rhs.getType()) &&
+ !assign.getTemporaryLhs()) {
fir::AliasAnalysis aliasAnalysis;
mlir::AliasResult aliasRes = aliasAnalysis.alias(lhs, rhs);
if (!aliasRes.isNo()) {
@@ -120,7 +118,14 @@ class InlineHLFIRAssignConversion
<< "\tLHS: " << lhs << "\n"
<< "\tRHS: " << rhs << "\n"
<< "\tALIAS: " << aliasRes << "\n");
- return rewriter.notifyMatchFailure(assign, "RHS/LHS may alias");
+ // Overlap is Unknown: unsafe to read RHS while writing LHS
+ // without a temp. Call genIndexBasedDisjointnessCheck(..) or
+ // genAddressBasedDisjointnessCheck(..) to check if the slices are
+ // disjoint.
+ // 1. If disjoint -> direct element-wise copy (no temp).
+ // 2. If not disjoint -> allocate a temporary and copy RHS into it,
+ // then copy the temporary to LHS..
+ rhsNeedsTemporary = true;
}
}
}
@@ -129,6 +134,58 @@ class InlineHLFIRAssignConversion
fir::FirOpBuilder builder(rewriter, assign.getOperation());
builder.setInsertionPoint(assign);
+ const bool useWorkshare = flangomp::shouldUseWorkshareLowering(assign);
+ mlir::ArrayAttr accessGroups;
+ if (auto attrs = assign.getOperation()->getAttrOfType<mlir::ArrayAttr>(
+ fir::getAccessGroupsAttrName()))
+ accessGroups = attrs;
+
+ auto emitAssignFrom = [&](hlfir::Entity rhsEntity) {
+ hlfir::genNoAliasArrayAssignment(
+ loc, builder, rhsEntity, lhs, useWorkshare,
+ /*temporaryLHS=*/false, nullptr, accessGroups);
+ };
+
+ if (rhsNeedsTemporary) {
+ std::optional<mlir::Value> disjoint =
+ fir::factory::genIndexBasedDisjointnessCheck(loc, builder, lhs, rhs);
+ if (!disjoint) {
+ disjoint = fir::factory::genAddressBasedDisjointnessCheck(loc, builder,
+ lhs, rhs);
+ }
+ if (!disjoint)
+ return rewriter.notifyMatchFailure(
+ assign, "Failed to generate runtime disjointness check,"
+ "deferring to runtime assignment implementation");
+
+ builder.genIfThenElse(loc, *disjoint)
+ .genThen([&]() { emitAssignFrom(rhs); })
+ .genElse([&]() {
+ auto [temp, isHeapAlloc] =
+ hlfir::createTempFromMold(loc, builder, lhs);
+ hlfir::genNoAliasArrayAssignment(
+ loc, builder, rhs, temp, useWorkshare,
+ /*temporaryLHS=*/true, nullptr, accessGroups);
+ emitAssignFrom(temp);
+ if (isHeapAlloc) {
+ auto seqTy = mlir::cast<fir::SequenceType>(
+ hlfir::getFortranElementOrSequenceType(lhs.getType()));
+ auto heapTy = fir::HeapType::get(seqTy);
+ mlir::Value heapAddr;
+ if (mlir::isa<fir::BaseBoxType>(temp.getType())) {
+ mlir::Value addr = fir::BoxAddrOp::create(builder, loc, temp);
+ heapAddr = fir::ConvertOp::create(builder, loc, heapTy, addr);
+ } else {
+ heapAddr = fir::ConvertOp::create(builder, loc, heapTy, temp);
+ }
+ fir::FreeMemOp::create(builder, loc, heapAddr);
+ }
+ })
+ .end();
+ rewriter.eraseOp(assign);
+ return mlir::success();
+ }
+
// Materialize scalar RHS before the assignment loop. Fortran 10.2.1.3
// requires that the RHS expression is fully evaluated before any part
// of the LHS variable is defined. When the scalar RHS is a reference
@@ -137,13 +194,8 @@ class InlineHLFIRAssignConversion
if (!rhs.isArray())
rhs = hlfir::loadTrivialScalar(loc, builder, rhs);
- mlir::ArrayAttr accessGroups;
- if (auto attrs = assign.getOperation()->getAttrOfType<mlir::ArrayAttr>(
- fir::getAccessGroupsAttrName()))
- accessGroups = attrs;
- hlfir::genNoAliasArrayAssignment(
- loc, builder, rhs, lhs, flangomp::shouldUseWorkshareLowering(assign),
- /*temporaryLHS=*/false, /*combiner=*/nullptr, accessGroups);
+ emitAssignFrom(rhs);
+
rewriter.eraseOp(assign);
return mlir::success();
}
diff --git a/flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir b/flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir
new file mode 100644
index 0000000000000..1a44713fcb798
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir
@@ -0,0 +1,140 @@
+// Test inlining of hlfir.assign for pointer-based array assignment
+
+// RUN: fir-opt --inline-hlfir-assign %s | FileCheck %s
+
+// Fortran source code:
+// subroutine assign_pointers(arr, N, lo1, hi1, lo2, hi2)
+// integer, intent(in) :: N, lo1, hi1, lo2, hi2
+// integer, pointer, intent(inout) :: arr(:)
+// integer, pointer :: p1(:), p2(:)
+//
+// p1 => arr(lo1:hi1)
+// p2 => arr(lo2:hi2)
+//
+// p1 = p2
+// end subroutine assign_pointers
+
+// CHECK-LABEL: func.func @_QPassign_pointers
+
+// Address-based runtime disjointness check:
+// CHECK: %[[LHS_ADDR:.*]] = fir.box_addr %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> !fir.ptr<!fir.array<?xi32>>
+// CHECK: %[[LHS_BASE:.*]] = fir.convert %[[LHS_ADDR]] : (!fir.ptr<!fir.array<?xi32>>) -> i64
+// CHECK: fir.box_elesize %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> index
+// CHECK: fir.box_dims %{{.*}}, %{{.*}}
+// CHECK: arith.subi %{{.*}}, %{{.*}} : index
+// CHECK: arith.muli %{{.*}}, %{{.*}} : index
+// CHECK: arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: arith.subi %{{.*}}, %{{.*}} : index
+// CHECK: arith.addi %{{.*}}, %{{.*}} : index
+// CHECK: fir.convert %{{.*}} : (index) -> i64
+// CHECK: fir.convert %{{.*}} : (index) -> i64
+// CHECK: %[[LHS_START:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+// CHECK: %[[LHS_END:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+// CHECK: %[[RHS_ADDR:.*]] = fir.box_addr %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> !fir.ptr<!fir.array<?xi32>>
+// CHECK: %[[RHS_BASE:.*]] = fir.convert %[[RHS_ADDR]] : (!fir.ptr<!fir.array<?xi32>>) -> i64
+// CHECK: fir.box_elesize %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> index
+// CHECK: fir.box_dims %{{.*}}, %{{.*}}
+// CHECK: arith.subi %{{.*}}, %{{.*}} : index
+// CHECK: arith.muli %{{.*}}, %{{.*}} : index
+// CHECK: arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: arith.subi %{{.*}}, %{{.*}} : index
+// CHECK: arith.addi %{{.*}}, %{{.*}} : index
+// CHECK: fir.convert %{{.*}} : (index) -> i64
+// CHECK: fir.convert %{{.*}} : (index) -> i64
+// CHECK: %[[RHS_START:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+// CHECK: %[[RHS_END:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+// CHECK: %[[CMP1:.*]] = arith.cmpi ult, %[[LHS_END]], %[[RHS_START]] : i64
+// CHECK: %[[CMP2:.*]] = arith.cmpi ult, %[[RHS_END]], %[[LHS_START]] : i64
+// CHECK: %[[DISJOINT:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+// CHECK: fir.if %[[DISJOINT]] {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate
+// CHECK: fir.load
+// CHECK: hlfir.designate
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+// CHECK: }
+// CHECK: } else {
+// CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<?xi32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+// CHECK: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate
+// CHECK: fir.load
+// CHECK: hlfir.designate %[[TEMP]]#0
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+// CHECK: }
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate %[[TEMP]]#0
+// CHECK: fir.load
+// CHECK: hlfir.designate
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+// CHECK: }
+// CHECK: fir.freemem %{{.*}}
+// CHECK: }
+
+func.func @_QPassign_pointers(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> {fir.bindc_name = "arr"}, %arg1: !fir.ref<i32> {fir.bindc_name = "n"}, %arg2: !fir.ref<i32> {fir.bindc_name = "lo1"}, %arg3: !fir.ref<i32> {fir.bindc_name = "hi1"}, %arg4: !fir.ref<i32> {fir.bindc_name = "lo2"}, %arg5: !fir.ref<i32> {fir.bindc_name = "hi2"}) {
+ %0 = fir.dummy_scope : !fir.dscope
+ %1:2 = hlfir.declare %arg0 dummy_scope %0 arg 1 {fortran_attrs = #fir.var_attrs<intent_inout, pointer>, uniq_name = "_QFassign_pointersEarr"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.dscope) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>)
+ %2:2 = hlfir.declare %arg3 dummy_scope %0 arg 4 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFassign_pointersEhi1"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %3:2 = hlfir.declare %arg5 dummy_scope %0 arg 6 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFassign_pointersEhi2"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %4:2 = hlfir.declare %arg2 dummy_scope %0 arg 3 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFassign_pointersElo1"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %5:2 = hlfir.declare %arg4 dummy_scope %0 arg 5 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFassign_pointersElo2"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %6:2 = hlfir.declare %arg1 dummy_scope %0 arg 2 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFassign_pointersEn"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %7 = fir.alloca !fir.box<!fir.ptr<!fir.array<?xi32>>> {bindc_name = "p1", uniq_name = "_QFassign_pointersEp1"}
+ %8 = fir.zero_bits !fir.ptr<!fir.array<?xi32>>
+ %c0 = arith.constant 0 : index
+ %9 = fir.shape %c0 : (index) -> !fir.shape<1>
+ %10 = fir.embox %8(%9) : (!fir.ptr<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.ptr<!fir.array<?xi32>>>
+ fir.store %10 to %7 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %11:2 = hlfir.declare %7 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFassign_pointersEp1"} : (!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>>>>)
+ %12 = fir.alloca !fir.box<!fir.ptr<!fir.array<?xi32>>> {bindc_name = "p2", uniq_name = "_QFassign_pointersEp2"}
+ %13 = fir.zero_bits !fir.ptr<!fir.array<?xi32>>
+ %c0_0 = arith.constant 0 : index
+ %14 = fir.shape %c0_0 : (index) -> !fir.shape<1>
+ %15 = fir.embox %13(%14) : (!fir.ptr<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.ptr<!fir.array<?xi32>>>
+ fir.store %15 to %12 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %16:2 = hlfir.declare %12 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFassign_pointersEp2"} : (!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>>>>)
+ %17 = fir.load %1#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %18 = fir.load %4#0 : !fir.ref<i32>
+ %19 = fir.convert %18 : (i32) -> i64
+ %20 = fir.load %2#0 : !fir.ref<i32>
+ %21 = fir.convert %20 : (i32) -> i64
+ %22 = fir.convert %19 : (i64) -> index
+ %23 = fir.convert %21 : (i64) -> index
+ %c1 = arith.constant 1 : index
+ %c0_1 = arith.constant 0 : index
+ %24 = arith.subi %23, %22 : index
+ %25 = arith.addi %24, %c1 : index
+ %26 = arith.divsi %25, %c1 : index
+ %27 = arith.cmpi sgt, %26, %c0_1 : index
+ %28 = arith.select %27, %26, %c0_1 : index
+ %29 = fir.shape %28 : (index) -> !fir.shape<1>
+ %30 = hlfir.designate %17 (%22:%23:%c1) shape %29 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+ %31 = fir.rebox %30 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.ptr<!fir.array<?xi32>>>
+ fir.store %31 to %11#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %32 = fir.load %1#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %33 = fir.load %5#0 : !fir.ref<i32>
+ %34 = fir.convert %33 : (i32) -> i64
+ %35 = fir.load %3#0 : !fir.ref<i32>
+ %36 = fir.convert %35 : (i32) -> i64
+ %37 = fir.convert %34 : (i64) -> index
+ %38 = fir.convert %36 : (i64) -> index
+ %c1_2 = arith.constant 1 : index
+ %c0_3 = arith.constant 0 : index
+ %39 = arith.subi %38, %37 : index
+ %40 = arith.addi %39, %c1_2 : index
+ %41 = arith.divsi %40, %c1_2 : index
+ %42 = arith.cmpi sgt, %41, %c0_3 : index
+ %43 = arith.select %42, %41, %c0_3 : index
+ %44 = fir.shape %43 : (index) -> !fir.shape<1>
+ %45 = hlfir.designate %32 (%37:%38:%c1_2) shape %44 : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+ %46 = fir.rebox %45 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.ptr<!fir.array<?xi32>>>
+ fir.store %46 to %16#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %47 = fir.load %16#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ %48 = fir.load %11#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+ hlfir.assign %47 to %48 : !fir.box<!fir.ptr<!fir.array<?xi32>>>, !fir.box<!fir.ptr<!fir.array<?xi32>>>
+ return
+}
diff --git a/flang/test/HLFIR/inline-hlfir-assign-scalar-index.fir b/flang/test/HLFIR/inline-hlfir-assign-scalar-index.fir
new file mode 100644
index 0000000000000..49b64bec212c3
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-scalar-index.fir
@@ -0,0 +1,106 @@
+// Test inlining of hlfir.assign for self-copy with scalar index and triplet dimensions.
+// RUN: fir-opt --inline-hlfir-assign %s | FileCheck %s
+
+// Fortran source code:
+// subroutine copy_row(a, n, i, j, lo1, hi1, lo2, hi2)
+// integer, intent(in) :: n, i, j, lo1, hi1, lo2, hi2
+// real, intent(inout) :: a(n, n)
+// a(i, lo1:hi1) = a(j, lo2:hi2)
+// end subroutine copy_row
+
+// CHECK-LABEL: func.func @_QPcopy_row
+
+// Index-based disjointness check for scalar index (dim1) and triplet (dim2):
+// Dim1 (scalar): compares i vs j : disjoint if i != j (i.e., i < j || j < i)
+// CHECK: %[[CMP1:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[CMP2:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[DISJ_DIM1:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+
+// Dim2 (triplet): compares lo1:hi1 vs lo2:hi2 — disjoint if hi1 < lo2 || hi2 < lo1
+// CHECK: %[[CMP3:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[CMP4:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[DISJ_DIM2:.*]] = arith.ori %[[CMP3]], %[[CMP4]] : i1
+// Overall: disjoint if ANY dimension is disjoint
+// CHECK: %[[DISJOINT:.*]] = arith.ori %[[DISJ_DIM1]], %[[DISJ_DIM2]] : i1
+// CHECK: fir.if %[[DISJOINT]] {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate
+// CHECK: fir.load
+// CHECK: hlfir.designate
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: } else {
+// CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<?xf32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+// CHECK: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate
+// CHECK: fir.load
+// CHECK: hlfir.designate %[[TEMP]]#0
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: hlfir.designate %[[TEMP]]#0
+// CHECK: fir.load
+// CHECK: hlfir.designate
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: fir.freemem %{{.*}}
+// CHECK: }
+
+func.func @_QPcopy_row(%arg0: !fir.ref<!fir.array<?x?xf32>> {fir.bindc_name = "a"}, %arg1: !fir.ref<i32> {fir.bindc_name = "n"}, %arg2: !fir.ref<i32> {fir.bindc_name = "i"}, %arg3: !fir.ref<i32> {fir.bindc_name = "j"}, %arg4: !fir.ref<i32> {fir.bindc_name = "lo1"}, %arg5: !fir.ref<i32> {fir.bindc_name = "hi1"}, %arg6: !fir.ref<i32> {fir.bindc_name = "lo2"}, %arg7: !fir.ref<i32> {fir.bindc_name = "hi2"}) {
+ %c1 = arith.constant 1 : index
+ %c0 = arith.constant 0 : index
+ %0 = fir.dummy_scope : !fir.dscope
+ %1:2 = hlfir.declare %arg1 dummy_scope %0 arg 2 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowEn"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %2:2 = hlfir.declare %arg2 dummy_scope %0 arg 3 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowEi"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %3:2 = hlfir.declare %arg3 dummy_scope %0 arg 4 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowEj"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %4:2 = hlfir.declare %arg4 dummy_scope %0 arg 5 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowElo1"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %5:2 = hlfir.declare %arg5 dummy_scope %0 arg 6 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowEhi1"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %6:2 = hlfir.declare %arg6 dummy_scope %0 arg 7 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowElo2"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %7:2 = hlfir.declare %arg7 dummy_scope %0 arg 8 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFcopy_rowEhi2"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+ %8 = fir.load %1#0 : !fir.ref<i32>
+ %9 = fir.convert %8 : (i32) -> i64
+ %10 = fir.convert %9 : (i64) -> index
+ %11 = arith.cmpi sgt, %10, %c0 : index
+ %12 = arith.select %11, %10, %c0 : index
+ %13 = fir.shape %12, %12 : (index, index) -> !fir.shape<2>
+ %14:2 = hlfir.declare %arg0(%13) dummy_scope %0 arg 1 {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QFcopy_rowEa"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>, !fir.dscope) -> (!fir.box<!fir.array<?x?xf32>>, !fir.ref<!fir.array<?x?xf32>>)
+
+ // RHS: a(j, lo2:hi2)
+ %15 = fir.load %3#0 : !fir.ref<i32>
+ %16 = fir.convert %15 : (i32) -> i64
+ %17 = fir.convert %16 : (i64) -> index
+ %18 = fir.load %6#0 : !fir.ref<i32>
+ %19 = fir.convert %18 : (i32) -> i64
+ %20 = fir.convert %19 : (i64) -> index
+ %21 = fir.load %7#0 : !fir.ref<i32>
+ %22 = fir.convert %21 : (i32) -> i64
+ %23 = fir.convert %22 : (i64) -> index
+ %24 = arith.subi %23, %20 : index
+ %25 = arith.addi %24, %c1 : index
+ %26 = arith.cmpi sgt, %25, %c0 : index
+ %27 = arith.select %26, %25, %c0 : index
+ %28 = fir.shape %27 : (index) -> !fir.shape<1>
+ %29 = hlfir.designate %14#0 (%17, %20:%23:%c1) shape %28 : (!fir.box<!fir.array<?x?xf32>>, index, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+
+ // LHS: a(i, lo1:hi1)
+ %30 = fir.load %2#0 : !fir.ref<i32>
+ %31 = fir.convert %30 : (i32) -> i64
+ %32 = fir.convert %31 : (i64) -> index
+ %33 = fir.load %4#0 : !fir.ref<i32>
+ %34 = fir.convert %33 : (i32) -> i64
+ %35 = fir.convert %34 : (i64) -> index
+ %36 = fir.load %5#0 : !fir.ref<i32>
+ %37 = fir.convert %36 : (i32) -> i64
+ %38 = fir.convert %37 : (i64) -> index
+ %39 = arith.subi %38, %35 : index
+ %40 = arith.addi %39, %c1 : index
+ %41 = arith.cmpi sgt, %40, %c0 : index
+ %42 = arith.select %41, %40, %c0 : index
+ %43 = fir.shape %42 : (index) -> !fir.shape<1>
+ %44 = hlfir.designate %14#0 (%32, %35:%38:%c1) shape %43 : (!fir.box<!fir.array<?x?xf32>>, index, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+
+ hlfir.assign %29 to %44 : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
+ return
+}
diff --git a/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir b/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
new file mode 100644
index 0000000000000..ea9435c343074
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
@@ -0,0 +1,149 @@
+// Test inlining of hlfir.assign for array sections with the same runtime stride on LHS and RHS
+// RUN: fir-opt --inline-hlfir-assign %s | FileCheck %s
+
+// Fortran source:
+// subroutine testcase(a, n, step)
+// implicit none
+// integer, intent(in) :: n, step
+// real, intent(inout) :: a(n)
+// integer :: lo1, hi1, lo2, hi2
+// lo1 = 1
+// hi1 = max(n / 2, 1)
+// lo2 = 2
+// hi2 = n
+// a(lo1:hi1:step) = a(lo2:hi2:step)
+// end subroutine testcase
+//
+
+// CHECK-LABEL: func.func @_QPtestcase
+// CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.array<?xf32>>
+
+// CHECK: %{{.*}} = hlfir.designate
+// CHECK: %{{.*}} = hlfir.designate
+// CHECK: %[[LHS_ADDR:.*]] = fir.box_addr %{{.*}}
+// CHECK: %[[LHS_BASE:.*]] = fir.convert %[[LHS_ADDR]] : (!fir.ref<!fir.array<?xf32>>) -> i64
+// CHECK: %[[LHS_ELESZ:.*]] = fir.box_elesize %{{.*}}
+// CHECK: %[[LHS_DIMS:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}}
+// CHECK: %{{.*}} = arith.subi %[[LHS_DIMS]]#1, %{{.*}} : index
+// CHECK: %{{.*}} = arith.muli %{{.*}}, %[[LHS_DIMS]]#2 : index
+// CHECK: %{{.*}} = arith.cmpi slt, %[[LHS_DIMS]]#2, %{{.*}} : index
+// CHECK: %{{.*}} = arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = arith.subi %[[LHS_ELESZ]], %{{.*}} : index
+// CHECK: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = fir.convert %{{.*}} : (index) -> i64
+// CHECK: %{{.*}} = fir.convert %{{.*}} : (index) -> i64
+// CHECK: %[[LHS_START:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+// CHECK: %[[LHS_END:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+// CHECK: %[[RHS_ADDR:.*]] = fir.box_addr %{{.*}}
+// CHECK: %[[RHS_BASE:.*]] = fir.convert %[[RHS_ADDR]] : (!fir.ref<!fir.array<?xf32>>) -> i64
+// CHECK: %[[RHS_ELESZ:.*]] = fir.box_elesize %{{.*}}
+// CHECK: %[[RHS_DIMS:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}}
+// CHECK: %{{.*}} = arith.subi %[[RHS_DIMS]]#1, %{{.*}} : index
+// CHECK: %{{.*}} = arith.muli %{{.*}}, %[[RHS_DIMS]]#2 : index
+// CHECK: %{{.*}} = arith.cmpi slt, %[[RHS_DIMS]]#2, %{{.*}} : index
+// CHECK: %{{.*}} = arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = arith.select %{{.*}}, %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = arith.subi %[[RHS_ELESZ]], %{{.*}} : index
+// CHECK: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : index
+// CHECK: %{{.*}} = fir.convert %{{.*}} : (index) -> i64
+// CHECK: %{{.*}} = fir.convert %{{.*}} : (index) -> i64
+// CHECK: %[[RHS_START:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+// CHECK: %[[RHS_END:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+// CHECK: %[[CMP1:.*]] = arith.cmpi ult, %[[LHS_END]], %[[RHS_START]] : i64
+// CHECK: %[[CMP2:.*]] = arith.cmpi ult, %[[RHS_END]], %[[LHS_START]] : i64
+// CHECK: %[[DISJOINT:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+// CHECK: fir.if %[[DISJOINT]] {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %{{.*}} (%{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %{{.*}} (%{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}}
+// CHECK: }
+// CHECK: } else {
+// CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<?xf32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+// CHECK: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %{{.*}} (%{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %[[TEMP]]#0 (%{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs
+// CHECK: }
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %[[TEMP]]#0 (%{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %{{.*}} (%{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}}
+// CHECK: }
+// CHECK: fir.freemem %{{.*}}
+// CHECK: }
+
+func.func @_QPtestcase(%arg0: !fir.ref<!fir.array<?xf32>> {fir.bindc_name = "a"}, %arg1: !fir.ref<i32> {fir.bindc_name = "n"}, %arg2: !fir.ref<i32> {fir.bindc_name = "step"}) {
+ %0 = fir.dummy_scope : !fir.dscope
+ %1:2 = hlfir.declare %arg1 dummy_scope %0 arg 2 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtestcaseEn"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %2 = fir.alloca i32 {bindc_name = "hi1", uniq_name = "_QFtestcaseEhi1"}
+ %3:2 = hlfir.declare %2 {uniq_name = "_QFtestcaseEhi1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %4 = fir.alloca i32 {bindc_name = "hi2", uniq_name = "_QFtestcaseEhi2"}
+ %5:2 = hlfir.declare %4 {uniq_name = "_QFtestcaseEhi2"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %6 = fir.alloca i32 {bindc_name = "lo1", uniq_name = "_QFtestcaseElo1"}
+ %7:2 = hlfir.declare %6 {uniq_name = "_QFtestcaseElo1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %8 = fir.alloca i32 {bindc_name = "lo2", uniq_name = "_QFtestcaseElo2"}
+ %9:2 = hlfir.declare %8 {uniq_name = "_QFtestcaseElo2"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %10:2 = hlfir.declare %arg2 dummy_scope %0 arg 3 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtestcaseEstep"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %11 = fir.load %1#0 : !fir.ref<i32>
+ %12 = fir.convert %11 : (i32) -> i64
+ %13 = fir.convert %12 : (i64) -> index
+ %c0 = arith.constant 0 : index
+ %14 = arith.cmpi sgt, %13, %c0 : index
+ %15 = arith.select %14, %13, %c0 : index
+ %16 = fir.shape %15 : (index) -> !fir.shape<1>
+ %17:2 = hlfir.declare %arg0(%16) dummy_scope %0 arg 1 {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QFtestcaseEa"} : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.ref<!fir.array<?xf32>>)
+ %c1_i32 = arith.constant 1 : i32
+ hlfir.assign %c1_i32 to %7#0 : i32, !fir.ref<i32>
+ %18 = fir.load %1#0 : !fir.ref<i32>
+ %c2_i32 = arith.constant 2 : i32
+ %19 = arith.divsi %18, %c2_i32 : i32
+ %c1_i32_0 = arith.constant 1 : i32
+ %20 = arith.maxsi %19, %c1_i32_0 : i32
+ hlfir.assign %20 to %3#0 : i32, !fir.ref<i32>
+ %c2_i32_1 = arith.constant 2 : i32
+ hlfir.assign %c2_i32_1 to %9#0 : i32, !fir.ref<i32>
+ %21 = fir.load %1#0 : !fir.ref<i32>
+ hlfir.assign %21 to %5#0 : i32, !fir.ref<i32>
+ %22 = fir.load %9#0 : !fir.ref<i32>
+ %23 = fir.convert %22 : (i32) -> i64
+ %24 = fir.load %5#0 : !fir.ref<i32>
+ %25 = fir.convert %24 : (i32) -> i64
+ %26 = fir.convert %23 : (i64) -> index
+ %27 = fir.convert %25 : (i64) -> index
+ %28 = fir.load %10#0 : !fir.ref<i32>
+ %29 = fir.convert %28 : (i32) -> i64
+ %30 = fir.convert %29 : (i64) -> index
+ %c0_2 = arith.constant 0 : index
+ %31 = arith.subi %27, %26 : index
+ %32 = arith.addi %31, %30 : index
+ %33 = arith.divsi %32, %30 : index
+ %34 = arith.cmpi sgt, %33, %c0_2 : index
+ %35 = arith.select %34, %33, %c0_2 : index
+ %36 = fir.shape %35 : (index) -> !fir.shape<1>
+ %37 = hlfir.designate %17#0 (%26:%27:%30) shape %36 : (!fir.box<!fir.array<?xf32>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+ %38 = fir.load %7#0 : !fir.ref<i32>
+ %39 = fir.convert %38 : (i32) -> i64
+ %40 = fir.load %3#0 : !fir.ref<i32>
+ %41 = fir.convert %40 : (i32) -> i64
+ %42 = fir.convert %39 : (i64) -> index
+ %43 = fir.convert %41 : (i64) -> index
+ %44 = fir.load %10#0 : !fir.ref<i32>
+ %45 = fir.convert %44 : (i32) -> i64
+ %46 = fir.convert %45 : (i64) -> index
+ %c0_3 = arith.constant 0 : index
+ %47 = arith.subi %43, %42 : index
+ %48 = arith.addi %47, %46 : index
+ %49 = arith.divsi %48, %46 : index
+ %50 = arith.cmpi sgt, %49, %c0_3 : index
+ %51 = arith.select %50, %49, %c0_3 : index
+ %52 = fir.shape %51 : (index) -> !fir.shape<1>
+ %53 = hlfir.designate %17#0 (%42:%43:%46) shape %52 : (!fir.box<!fir.array<?xf32>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xf32>>
+ hlfir.assign %37 to %53 : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
+ return
+}
diff --git a/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir b/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
new file mode 100644
index 0000000000000..a1e2e996238c8
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
@@ -0,0 +1,190 @@
+// Test inlining of hlfir.assign for self-copy array section
+
+// RUN: fir-opt --inline-hlfir-assign %s | FileCheck %s
+
+// Fortran source code:
+// subroutine self_copy_ew(field, xm, ym, levs, xh, yh)
+// integer, intent(in) :: xm, ym, levs, xh, yh
+// real, intent(inout) :: field(xm + 2 * xh, ym + 2 * yh, levs)
+// integer :: x1, x2, x5, x6
+// x1 = 1
+// x2 = xh
+// x5 = xm + 1
+// x6 = xm + xh
+// field(x1:x2, :, :) = field(x5:x6, :, :)
+// end subroutine self_copy_ew
+
+// CHECK-LABEL: func.func @_QPself_copy_ew
+// CHECK-SAME: %[[ARGF:.*]]: !fir.ref<!fir.array<?x?x?xf32>> {fir.bindc_name = "field"}
+
+// CHECK: hlfir.declare %[[ARGF]](%{{.*}}) {{.*}} uniq_name = "_QFself_copy_ewEfield"
+
+// Index-based runtime disjointness check:
+// CHECK: %[[RHS:.*]] = hlfir.designate %{{.*}}#0 (%{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}) shape %{{.*}}
+// CHECK: %[[LHS:.*]] = hlfir.designate %{{.*}}#0 (%{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}) shape %{{.*}}
+// CHECK: %[[CMP1:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[CMP2:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[DIM1_DISJ:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+// CHECK: %[[CMP3:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[CMP4:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[DIM2_DISJ:.*]] = arith.ori %[[CMP3]], %[[CMP4]] : i1
+// CHECK: %[[DISJ12:.*]] = arith.ori %[[DIM1_DISJ]], %[[DIM2_DISJ]] : i1
+// CHECK: %[[CMP5:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[CMP6:.*]] = arith.cmpi slt, %{{.*}}, %{{.*}} : index
+// CHECK: %[[DIM3_DISJ:.*]] = arith.ori %[[CMP5]], %[[CMP6]] : i1
+// CHECK: %[[DISJOINT:.*]] = arith.ori %[[DISJ12]], %[[DIM3_DISJ]] : i1
+// CHECK: fir.if %[[DISJOINT]] {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %[[RHS]] (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %[[LHS]] (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: }
+// CHECK: }
+// CHECK: } else {
+// CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<?x?x?xf32>{{.*}}
+// CHECK: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %[[RHS]] (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %[[TEMP]]#0 (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: }
+// CHECK: }
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK: %{{.*}} = hlfir.designate %[[TEMP]]#0 (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: %{{.*}} = fir.load %{{.*}}
+// CHECK: %{{.*}} = hlfir.designate %[[LHS]] (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: }
+// CHECK: }
+// CHECK: fir.freemem %{{.*}}
+// CHECK: }
+
+func.func @_QPself_copy_ew(%arg0: !fir.ref<!fir.array<?x?x?xf32>> {fir.bindc_name = "field"}, %arg1: !fir.ref<i32> {fir.bindc_name = "xm"}, %arg2: !fir.ref<i32> {fir.bindc_name = "ym"}, %arg3: !fir.ref<i32> {fir.bindc_name = "levs"}, %arg4: !fir.ref<i32> {fir.bindc_name = "xh"}, %arg5: !fir.ref<i32> {fir.bindc_name = "yh"}) {
+ %0 = fir.dummy_scope : !fir.dscope
+ %1:2 = hlfir.declare %arg1 dummy_scope %0 arg 2 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFself_copy_ewExm"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %2:2 = hlfir.declare %arg4 dummy_scope %0 arg 5 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFself_copy_ewExh"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %3:2 = hlfir.declare %arg2 dummy_scope %0 arg 3 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFself_copy_ewEym"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %4:2 = hlfir.declare %arg5 dummy_scope %0 arg 6 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFself_copy_ewEyh"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %5:2 = hlfir.declare %arg3 dummy_scope %0 arg 4 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFself_copy_ewElevs"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %6 = fir.alloca i32 {bindc_name = "x1", uniq_name = "_QFself_copy_ewEx1"}
+ %7:2 = hlfir.declare %6 {uniq_name = "_QFself_copy_ewEx1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %8 = fir.alloca i32 {bindc_name = "x2", uniq_name = "_QFself_copy_ewEx2"}
+ %9:2 = hlfir.declare %8 {uniq_name = "_QFself_copy_ewEx2"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %10 = fir.alloca i32 {bindc_name = "x5", uniq_name = "_QFself_copy_ewEx5"}
+ %11:2 = hlfir.declare %10 {uniq_name = "_QFself_copy_ewEx5"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %12 = fir.alloca i32 {bindc_name = "x6", uniq_name = "_QFself_copy_ewEx6"}
+ %13:2 = hlfir.declare %12 {uniq_name = "_QFself_copy_ewEx6"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %14 = fir.load %1#0 : !fir.ref<i32>
+ %c2_i32 = arith.constant 2 : i32
+ %15 = fir.load %2#0 : !fir.ref<i32>
+ %16 = arith.muli %c2_i32, %15 : i32
+ %17 = arith.addi %14, %16 : i32
+ %18 = fir.convert %17 : (i32) -> i64
+ %19 = fir.convert %18 : (i64) -> index
+ %c0 = arith.constant 0 : index
+ %20 = arith.cmpi sgt, %19, %c0 : index
+ %21 = arith.select %20, %19, %c0 : index
+ %22 = fir.load %3#0 : !fir.ref<i32>
+ %c2_i32_0 = arith.constant 2 : i32
+ %23 = fir.load %4#0 : !fir.ref<i32>
+ %24 = arith.muli %c2_i32_0, %23 : i32
+ %25 = arith.addi %22, %24 : i32
+ %26 = fir.convert %25 : (i32) -> i64
+ %27 = fir.convert %26 : (i64) -> index
+ %c0_1 = arith.constant 0 : index
+ %28 = arith.cmpi sgt, %27, %c0_1 : index
+ %29 = arith.select %28, %27, %c0_1 : index
+ %30 = fir.load %5#0 : !fir.ref<i32>
+ %31 = fir.convert %30 : (i32) -> i64
+ %32 = fir.convert %31 : (i64) -> index
+ %c0_2 = arith.constant 0 : index
+ %33 = arith.cmpi sgt, %32, %c0_2 : index
+ %34 = arith.select %33, %32, %c0_2 : index
+ %35 = fir.shape %21, %29, %34 : (index, index, index) -> !fir.shape<3>
+ %36:2 = hlfir.declare %arg0(%35) dummy_scope %0 arg 1 {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QFself_copy_ewEfield"} : (!fir.ref<!fir.array<?x?x?xf32>>, !fir.shape<3>, !fir.dscope) -> (!fir.box<!fir.array<?x?x?xf32>>, !fir.ref<!fir.array<?x?x?xf32>>)
+ %c1_i32 = arith.constant 1 : i32
+ hlfir.assign %c1_i32 to %7#0 : i32, !fir.ref<i32>
+ %37 = fir.load %2#0 : !fir.ref<i32>
+ hlfir.assign %37 to %9#0 : i32, !fir.ref<i32>
+ %38 = fir.load %1#0 : !fir.ref<i32>
+ %c1_i32_3 = arith.constant 1 : i32
+ %39 = arith.addi %38, %c1_i32_3 : i32
+ hlfir.assign %39 to %11#0 : i32, !fir.ref<i32>
+ %40 = fir.load %1#0 : !fir.ref<i32>
+ %41 = fir.load %2#0 : !fir.ref<i32>
+ %42 = arith.addi %40, %41 : i32
+ hlfir.assign %42 to %13#0 : i32, !fir.ref<i32>
+ %43 = fir.load %11#0 : !fir.ref<i32>
+ %44 = fir.convert %43 : (i32) -> i64
+ %45 = fir.load %13#0 : !fir.ref<i32>
+ %46 = fir.convert %45 : (i32) -> i64
+ %47 = fir.convert %44 : (i64) -> index
+ %48 = fir.convert %46 : (i64) -> index
+ %c1 = arith.constant 1 : index
+ %c0_4 = arith.constant 0 : index
+ %49 = arith.subi %48, %47 : index
+ %50 = arith.addi %49, %c1 : index
+ %51 = arith.divsi %50, %c1 : index
+ %52 = arith.cmpi sgt, %51, %c0_4 : index
+ %53 = arith.select %52, %51, %c0_4 : index
+ %c1_5 = arith.constant 1 : index
+ %c1_6 = arith.constant 1 : index
+ %c0_7 = arith.constant 0 : index
+ %54 = arith.subi %29, %c1_5 : index
+ %55 = arith.addi %54, %c1_6 : index
+ %56 = arith.divsi %55, %c1_6 : index
+ %57 = arith.cmpi sgt, %56, %c0_7 : index
+ %58 = arith.select %57, %56, %c0_7 : index
+ %c1_8 = arith.constant 1 : index
+ %c0_9 = arith.constant 0 : index
+ %59 = arith.subi %34, %c1_5 : index
+ %60 = arith.addi %59, %c1_8 : index
+ %61 = arith.divsi %60, %c1_8 : index
+ %62 = arith.cmpi sgt, %61, %c0_9 : index
+ %63 = arith.select %62, %61, %c0_9 : index
+ %64 = fir.shape %53, %58, %63 : (index, index, index) -> !fir.shape<3>
+ %65 = hlfir.designate %36#0 (%47:%48:%c1, %c1_5:%29:%c1_6, %c1_5:%34:%c1_8) shape %64 : (!fir.box<!fir.array<?x?x?xf32>>, index, index, index, index, index, index, index, index, index, !fir.shape<3>) -> !fir.box<!fir.array<?x?x?xf32>>
+ %66 = fir.load %7#0 : !fir.ref<i32>
+ %67 = fir.convert %66 : (i32) -> i64
+ %68 = fir.load %9#0 : !fir.ref<i32>
+ %69 = fir.convert %68 : (i32) -> i64
+ %70 = fir.convert %67 : (i64) -> index
+ %71 = fir.convert %69 : (i64) -> index
+ %c1_10 = arith.constant 1 : index
+ %c0_11 = arith.constant 0 : index
+ %72 = arith.subi %71, %70 : index
+ %73 = arith.addi %72, %c1_10 : index
+ %74 = arith.divsi %73, %c1_10 : index
+ %75 = arith.cmpi sgt, %74, %c0_11 : index
+ %76 = arith.select %75, %74, %c0_11 : index
+ %c1_12 = arith.constant 1 : index
+ %c1_13 = arith.constant 1 : index
+ %c0_14 = arith.constant 0 : index
+ %77 = arith.subi %29, %c1_12 : index
+ %78 = arith.addi %77, %c1_13 : index
+ %79 = arith.divsi %78, %c1_13 : index
+ %80 = arith.cmpi sgt, %79, %c0_14 : index
+ %81 = arith.select %80, %79, %c0_14 : index
+ %c1_15 = arith.constant 1 : index
+ %c0_16 = arith.constant 0 : index
+ %82 = arith.subi %34, %c1_12 : index
+ %83 = arith.addi %82, %c1_15 : index
+ %84 = arith.divsi %83, %c1_15 : index
+ %85 = arith.cmpi sgt, %84, %c0_16 : index
+ %86 = arith.select %85, %84, %c0_16 : index
+ %87 = fir.shape %76, %81, %86 : (index, index, index) -> !fir.shape<3>
+ %88 = hlfir.designate %36#0 (%70:%71:%c1_10, %c1_12:%29:%c1_13, %c1_12:%34:%c1_15) shape %87 : (!fir.box<!fir.array<?x?x?xf32>>, index, index, index, index, index, index, index, index, index, !fir.shape<3>) -> !fir.box<!fir.array<?x?x?xf32>>
+ hlfir.assign %65 to %88 : !fir.box<!fir.array<?x?x?xf32>>, !fir.box<!fir.array<?x?x?xf32>>
+ return
+}
diff --git a/flang/test/Lower/inline-hlfir-assign-forall-threadprivate.f90 b/flang/test/Lower/inline-hlfir-assign-forall-threadprivate.f90
new file mode 100644
index 0000000000000..da9300ef52537
--- /dev/null
+++ b/flang/test/Lower/inline-hlfir-assign-forall-threadprivate.f90
@@ -0,0 +1,119 @@
+! Test inlining of hlfir.assign for FORALL with threadprivate array self-assignment.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | fir-opt --inline-hlfir-assign --lower-hlfir-ordered-assignments --lower-hlfir-intrinsics --bufferize-hlfir | FileCheck %s --check-prefix=BUFFERIZE
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | fir-opt --inline-hlfir-assign --lower-hlfir-ordered-assignments --lower-hlfir-intrinsics --bufferize-hlfir --inline-hlfir-assign | FileCheck %s --check-prefix=INLINE_HLFIR
+
+module m1
+ integer, parameter :: k2 = 10, k4 = 100
+ integer :: a(2, k2, 2, k4)
+ !$omp threadprivate(a)
+contains
+ subroutine s1()
+ integer :: n2, n3, n
+ forall (n2=1:2, n3=1:2)
+ a(n2,:,n3,:) = a(n2,:,n3,:) + reshape([(n, n=1, k2*k4)], [k2, k4])
+ end forall
+ end subroutine
+end module
+
+!! =============================================================================
+!! After BufferizeHLFIR
+!! =============================================================================
+! BUFFERIZE-LABEL: func.func @_QMm1Ps1
+
+! Value stack creation:
+! BUFFERIZE: %[[VSTACK:.*]] = fir.call @_FortranACreateValueStack
+
+! Phase 1: evaluate RHS into temp and push to value stack
+! BUFFERIZE: fir.do_loop
+! BUFFERIZE: fir.do_loop
+! BUFFERIZE: fir.allocmem !fir.array<10x100xi32>
+! BUFFERIZE: hlfir.declare
+! BUFFERIZE: fir.do_loop {{.*}} unordered
+! BUFFERIZE: fir.do_loop {{.*}} unordered
+! BUFFERIZE: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+! BUFFERIZE: }
+! BUFFERIZE: }
+! BUFFERIZE: fir.call @_FortranAPushValue
+! BUFFERIZE: fir.freemem
+
+! BUFFERIZE: fir.do_loop
+! BUFFERIZE: fir.do_loop
+! BUFFERIZE: fir.call @_FortranAValueAt(%[[VSTACK]], %{{.*}}, %{{.*}})
+! BUFFERIZE: %[[RHS:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<10x100xi32>>>>
+! BUFFERIZE: %[[LHS:.*]] = hlfir.designate %{{.*}} (%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}) shape %{{.*}}
+! BUFFERIZE: hlfir.assign %[[RHS]] to %[[LHS]] : !fir.box<!fir.heap<!fir.array<10x100xi32>>>, !fir.box<!fir.array<10x100xi32>>
+
+! BUFFERIZE: fir.call @_FortranADestroyValueStack(%[[VSTACK]])
+
+!! =============================================================================
+!! After InlineHLFIRAssign
+!! =============================================================================
+! INLINE_HLFIR-LABEL: func.func @_QMm1Ps1
+
+! Value stack creation:
+! INLINE_HLFIR: %[[VSTACK:.*]] = fir.call @_FortranACreateValueStack
+
+! Phase 2 is now inlined with address-based disjointness check:
+! INLINE_HLFIR: fir.call @_FortranAValueAt(%[[VSTACK]], %{{.*}}, %{{.*}})
+! INLINE_HLFIR: %[[RHS:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<10x100xi32>>>>
+! INLINE_HLFIR: %[[LHS:.*]] = hlfir.designate %{{.*}} (%{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}, %{{.*}}, %{{.*}}:%{{.*}}:%{{.*}}) shape %{{.*}}
+
+! Address-based disjointness check:
+! INLINE_HLFIR: %[[LHS_ADDR:.*]] = fir.box_addr %[[LHS]]
+! INLINE_HLFIR: %[[LHS_BASE:.*]] = fir.convert %[[LHS_ADDR]] : {{.*}} -> i64
+! INLINE_HLFIR: fir.box_elesize %[[LHS]]
+! INLINE_HLFIR: fir.box_dims %[[LHS]], %{{.*}}
+! INLINE_HLFIR: fir.box_dims %[[LHS]], %{{.*}}
+! INLINE_HLFIR: %[[LHS_START:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+! INLINE_HLFIR: %[[LHS_END:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+! INLINE_HLFIR: %[[RHS_ADDR:.*]] = fir.box_addr %[[RHS]]
+! INLINE_HLFIR: %[[RHS_BASE:.*]] = fir.convert %[[RHS_ADDR]] : {{.*}} -> i64
+! INLINE_HLFIR: fir.box_elesize %[[RHS]]
+! INLINE_HLFIR: fir.box_dims %[[RHS]], %{{.*}}
+! INLINE_HLFIR: fir.box_dims %[[RHS]], %{{.*}}
+! INLINE_HLFIR: %[[RHS_START:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+! INLINE_HLFIR: %[[RHS_END:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+! INLINE_HLFIR: %[[CMP1:.*]] = arith.cmpi ult, %[[LHS_END]], %[[RHS_START]] : i64
+! INLINE_HLFIR: %[[CMP2:.*]] = arith.cmpi ult, %[[RHS_END]], %[[LHS_START]] : i64
+! INLINE_HLFIR: %[[DISJOINT:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+
+! direct element-wise copy
+! INLINE_HLFIR: fir.if %[[DISJOINT]] {
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: hlfir.designate %[[RHS]]
+! INLINE_HLFIR: fir.load
+! INLINE_HLFIR: hlfir.designate %[[LHS]]
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: }
+
+! copy through temporary
+! INLINE_HLFIR: } else {
+! INLINE_HLFIR: %[[TALLOC:.*]] = fir.allocmem !fir.array<10x100xi32>
+! INLINE_HLFIR: %[[TEMP:.*]]:2 = hlfir.declare %[[TALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: hlfir.designate %[[RHS]]
+! INLINE_HLFIR: fir.load
+! INLINE_HLFIR: hlfir.designate %[[TEMP]]#0
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: }
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: fir.do_loop {{.*}} unordered
+! INLINE_HLFIR: hlfir.designate %[[TEMP]]#0
+! INLINE_HLFIR: fir.load
+! INLINE_HLFIR: hlfir.designate %[[LHS]]
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: }
+! INLINE_HLFIR: fir.freemem %{{.*}}
+! INLINE_HLFIR: }
+
+! INLINE_HLFIR: fir.call @_FortranADestroyValueStack(%[[VSTACK]])
+
+! INLINE_HLFIR-NOT: hlfir.as_expr
+! INLINE_HLFIR-NOT: hlfir.apply
+! INLINE_HLFIR-NOT: hlfir.destroy
diff --git a/flang/test/Lower/inline-hlfir-assign-pointer-omp.f90 b/flang/test/Lower/inline-hlfir-assign-pointer-omp.f90
new file mode 100644
index 0000000000000..8782080f60db6
--- /dev/null
+++ b/flang/test/Lower/inline-hlfir-assign-pointer-omp.f90
@@ -0,0 +1,125 @@
+! Test inlining of hlfir.assign for pointer-based array assignment with OpenMP.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | fir-opt --inline-hlfir-assign | FileCheck %s --check-prefix=FIRST_INLINE
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | fir-opt --inline-hlfir-assign --bufferize-hlfir | FileCheck %s --check-prefix=BUFFERIZE
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | fir-opt --inline-hlfir-assign --bufferize-hlfir --inline-hlfir-assign | FileCheck %s --check-prefix=INLINE_HLFIR
+
+subroutine test()
+ integer,target,dimension(64):: a, b
+ integer,pointer:: sum(:)
+ integer :: i
+
+ b = (/(i,i=1,64)/)
+
+ !$omp parallel do private(sum)
+ do i=1, 32
+ sum => b(i:i+32)
+ a(i:i+32) = sum
+ end do
+ !$omp end parallel do
+end subroutine test
+
+!! =============================================================================
+!! After 1st InlineHLFIRAssign
+!! =============================================================================
+! FIRST_INLINE-LABEL: func.func @_QPtest
+! FIRST_INLINE: omp.parallel
+! FIRST_INLINE: %[[SUM:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+! FIRST_INLINE: %[[LHS:.*]] = hlfir.designate %{{.*}} (%{{.*}}:%{{.*}}:%{{.*}}) shape %{{.*}} : (!fir.ref<!fir.array<64xi32>>, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+! FIRST_INLINE: %[[LHS_ADDR:.*]] = fir.box_addr %[[LHS]]
+! FIRST_INLINE: %[[LHS_BASE:.*]] = fir.convert %[[LHS_ADDR]] : {{.*}} -> i64
+! FIRST_INLINE: fir.box_elesize %[[LHS]]
+! FIRST_INLINE: fir.box_dims %[[LHS]], %{{.*}}
+! FIRST_INLINE: %[[LHS_START:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+! FIRST_INLINE: %[[LHS_END:.*]] = arith.addi %[[LHS_BASE]], %{{.*}} : i64
+! FIRST_INLINE: %[[RHS_ADDR:.*]] = fir.box_addr %[[SUM]]
+! FIRST_INLINE: %[[RHS_BASE:.*]] = fir.convert %[[RHS_ADDR]] : {{.*}} -> i64
+! FIRST_INLINE: fir.box_elesize %[[SUM]]
+! FIRST_INLINE: fir.box_dims %[[SUM]], %{{.*}}
+! FIRST_INLINE: %[[RHS_START:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+! FIRST_INLINE: %[[RHS_END:.*]] = arith.addi %[[RHS_BASE]], %{{.*}} : i64
+! FIRST_INLINE: %[[CMP1:.*]] = arith.cmpi ult, %[[LHS_END]], %[[RHS_START]] : i64
+! FIRST_INLINE: %[[CMP2:.*]] = arith.cmpi ult, %[[RHS_END]], %[[LHS_START]] : i64
+! FIRST_INLINE: %[[DISJOINT:.*]] = arith.ori %[[CMP1]], %[[CMP2]] : i1
+! FIRST_INLINE: fir.if %[[DISJOINT]] {
+! FIRST_INLINE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! FIRST_INLINE: hlfir.designate %[[SUM]] (%{{.*}})
+! FIRST_INLINE: fir.load
+! FIRST_INLINE: hlfir.designate %[[LHS]] (%{{.*}})
+! FIRST_INLINE: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! FIRST_INLINE: }
+! FIRST_INLINE: } else {
+! FIRST_INLINE: %[[ALLOC:.*]] = fir.allocmem !fir.array<?xi32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+! FIRST_INLINE: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+! FIRST_INLINE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! FIRST_INLINE: hlfir.designate %[[SUM]] (%{{.*}})
+! FIRST_INLINE: fir.load
+! FIRST_INLINE: hlfir.designate %[[TEMP]]#0 (%{{.*}})
+! FIRST_INLINE: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+! FIRST_INLINE: }
+! FIRST_INLINE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! FIRST_INLINE: hlfir.designate %[[TEMP]]#0 (%{{.*}})
+! FIRST_INLINE: fir.load
+! FIRST_INLINE: hlfir.designate %[[LHS]] (%{{.*}})
+! FIRST_INLINE: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! FIRST_INLINE: }
+! FIRST_INLINE: fir.freemem %{{.*}}
+! FIRST_INLINE: }
+
+
+!! =============================================================================
+!! After BufferizeHLFIR
+!! =============================================================================
+! BUFFERIZE-LABEL: func.func @_QPtest
+! BUFFERIZE: omp.parallel
+! BUFFERIZE: fir.if %{{.*}} {
+! BUFFERIZE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! BUFFERIZE: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! BUFFERIZE: }
+! BUFFERIZE: } else {
+! BUFFERIZE: %[[ALLOC:.*]] = fir.allocmem !fir.array<?xi32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+! BUFFERIZE: %[[TEMP:.*]]:2 = hlfir.declare %[[ALLOC]](%{{.*}}) {uniq_name = ".tmp"}
+! BUFFERIZE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! BUFFERIZE: hlfir.designate %{{.*}} (%{{.*}})
+! BUFFERIZE: fir.load
+! BUFFERIZE: hlfir.designate %[[TEMP]]#0 (%{{.*}})
+! BUFFERIZE: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+! BUFFERIZE: }
+! BUFFERIZE: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! BUFFERIZE: hlfir.designate %[[TEMP]]#0 (%{{.*}})
+! BUFFERIZE: fir.load
+! BUFFERIZE: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! BUFFERIZE: }
+! BUFFERIZE: fir.freemem %{{.*}}
+! BUFFERIZE: }
+
+!! =============================================================================
+!! After 2nd InlineHLFIRAssign
+!! =============================================================================
+! INLINE_HLFIR-LABEL: func.func @_QPtest
+! INLINE_HLFIR: omp.parallel
+! INLINE_HLFIR: fir.if %{{.*}} {
+! INLINE_HLFIR: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: } else {
+! INLINE_HLFIR: %[[ALLOC2:.*]] = fir.allocmem !fir.array<?xi32>, %{{.*}} {bindc_name = ".tmp", uniq_name = ""}
+! INLINE_HLFIR: %[[TEMP2:.*]]:2 = hlfir.declare %[[ALLOC2]](%{{.*}}) {uniq_name = ".tmp"}
+! INLINE_HLFIR: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! INLINE_HLFIR: hlfir.designate %{{.*}} (%{{.*}})
+! INLINE_HLFIR: fir.load
+! INLINE_HLFIR: hlfir.designate %[[TEMP2]]#0 (%{{.*}})
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+! INLINE_HLFIR: hlfir.designate %[[TEMP2]]#0 (%{{.*}})
+! INLINE_HLFIR: fir.load
+! INLINE_HLFIR: hlfir.designate %{{.*}} (%{{.*}})
+! INLINE_HLFIR: hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+! INLINE_HLFIR: }
+! INLINE_HLFIR: fir.freemem %{{.*}}
+! INLINE_HLFIR: }
+
+! INLINE_HLFIR-NOT: hlfir.as_expr
+! INLINE_HLFIR-NOT: hlfir.apply
+! INLINE_HLFIR-NOT: hlfir.destroy
More information about the flang-commits
mailing list