[flang-commits] [flang] [Flang]Add support for inlining hlfir.assign operation where both LHS and RHS are slices of the same array (PR #204532)

Kaviya Rajendiran via flang-commits flang-commits at lists.llvm.org
Tue Jun 23 09:30:32 PDT 2026


https://github.com/kaviya2510 updated https://github.com/llvm/llvm-project/pull/204532

>From 7d09ededfdc572846526708daf8b5b51ee81bd38 Mon Sep 17 00:00:00 2001
From: Kaviya Rajendiran <kaviyara2000 at gmail.com>
Date: Thu, 18 Jun 2026 14:43:52 +0530
Subject: [PATCH 1/2] [Flang]Add support for inlining hlfir.assign operation
 where both LHS and RHS are slices of the same array

---
 .../Optimizer/Analysis/ArraySectionAnalyzer.h |  15 +-
 .../Analysis/ArraySectionAnalyzer.cpp         |  51 ++++++
 .../HLFIR/Transforms/InlineHLFIRAssign.cpp    |  65 ++++++--
 ...-hlfir-assign-self-copy-runtime-stride.fir | 102 ++++++++++++
 .../HLFIR/inline-hlfir-assign-self-copy.fir   | 148 ++++++++++++++++++
 5 files changed, 367 insertions(+), 14 deletions(-)
 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

diff --git a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
index e87e37c3c5590..5bf60c49621ab 100644
--- a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
+++ b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
@@ -9,13 +9,13 @@
 #ifndef FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
 #define FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
 
+#include "mlir/IR/Location.h"
 #include "mlir/IR/Operation.h"
 #include "mlir/IR/Value.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 
 namespace mlir {
-class Operation;
-class Value;
+class OpBuilder;
 } // namespace mlir
 
 namespace hlfir {
@@ -62,6 +62,17 @@ class ArraySectionAnalyzer {
   analyze(mlir::Value ref1, mlir::Value ref2,
           ValueEquivalenceCallback areKnownEquivalent = nullptr);
 
+  ///  Generate a runtime disjointness check for the given LHS and RHS.
+  ///  Check if the sections are disjoint using `ub1 < lb2 || ub2 < lb1`
+  ///  Returns true if the sections are disjoint, false otherwise.
+  ///  Returns nullptr if the operands are not a matching pair of
+  ///  hlfir.designate on the same base or no dimension has statically-known
+  ///  stride order.
+  static mlir::Value genRuntimeDisjointnessCheck(mlir::Location loc,
+                                                 mlir::OpBuilder &builder,
+                                                 mlir::Value lhsRef,
+                                                 mlir::Value rhsRef);
+
   static bool isDesignatingArrayInOrder(hlfir::DesignateOp designate,
                                         hlfir::ElementalOpInterface elemental);
 
diff --git a/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
index 6eec184b99a7f..f657d67289874 100644
--- a/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
+++ b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
@@ -11,6 +11,7 @@
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/IR/BuiltinTypes.h"
 #include "llvm/Support/Debug.h"
 
 #define DEBUG_TYPE "array-section-analyzer"
@@ -193,6 +194,56 @@ ArraySectionAnalyzer::analyze(mlir::Value ref1, mlir::Value ref2,
   return SlicesOverlapKind::Unknown;
 }
 
+mlir::Value ArraySectionAnalyzer::genRuntimeDisjointnessCheck(
+    mlir::Location loc, mlir::OpBuilder &builder, mlir::Value lhsRef,
+    mlir::Value rhsRef) {
+  auto des1 = lhsRef.getDefiningOp<hlfir::DesignateOp>();
+  auto des2 = rhsRef.getDefiningOp<hlfir::DesignateOp>();
+  if (!des1 || !des2)
+    return {};
+
+  if (des1.getMemref() != des2.getMemref())
+    return {};
+
+  if (des1.getComponent() != des2.getComponent() ||
+      des1.getComponentShape() != des2.getComponentShape() ||
+      des1.getSubstring() != des2.getSubstring() ||
+      des1.getComplexPart() != des2.getComplexPart() ||
+      des1.getTypeparams() != des2.getTypeparams())
+    return {};
+
+  if (des1.getIsTriplet().empty() ||
+      !llvm::equal(des1.getIsTriplet(), des2.getIsTriplet()))
+    return {};
+
+  mlir::Type idxTy = mlir::IndexType::get(des1.getContext());
+  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()) {
+    SectionDesc desc1 = readSectionDesc(des1It, isTriplet);
+    SectionDesc desc2 = readSectionDesc(des2It, isTriplet);
+    auto [lb1, ub1] = getOrderedBounds(desc1);
+    auto [lb2, ub2] = 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;
+  }
+  return disjoint;
+}
+
 bool ArraySectionAnalyzer::isLess(mlir::Value v1, mlir::Value v2) {
   auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
     auto *op = v.getDefiningOp();
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
index b795d0b46da85..55c67c6773553 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
@@ -103,11 +103,9 @@ class InlineHLFIRAssignConversion
       return rewriter.notifyMatchFailure(assign,
                                          "RHS/LHS element types mismatch");
 
+    bool rhsNeedsTemporary = false;
+
     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.
       fir::AliasAnalysis aliasAnalysis;
       mlir::AliasResult aliasRes = aliasAnalysis.alias(lhs, rhs);
       if (!aliasRes.isNo()) {
@@ -121,7 +119,15 @@ 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
+          // ArraySectionAnalyzer::genRuntimeDisjointnessCheck to check if the
+          // sections are disjoint.
+          // 1. Sections disjoint at runtime-> direct element-wise copy (no
+          // temp)
+          // 2. Sections are not disjoint at runtime  -> Allocate a temporary
+          // and copy RHS into it, then copy the temporary to LHS.
+          rhsNeedsTemporary = true;
         }
       }
     }
@@ -130,6 +136,43 @@ 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) {
+      if (mlir::Value disjoint =
+              fir::ArraySectionAnalyzer::genRuntimeDisjointnessCheck(
+                  loc, builder, lhs, rhs)) {
+        builder.genIfThenElse(loc, disjoint)
+            .genThen([&]() { emitAssignFrom(rhs); })
+            .genElse([&]() {
+              mlir::Value tempExpr = hlfir::AsExprOp::create(builder, loc, rhs);
+              emitAssignFrom(hlfir::Entity{tempExpr});
+              hlfir::DestroyOp::create(builder, loc, tempExpr);
+            })
+            .end();
+        rewriter.eraseOp(assign);
+        return mlir::success();
+      }
+    }
+
+    // When rhsNeedsTemporary is true and genRuntimeDisjointnessCheck
+    // returned null, always use a temporary rhsExpr.
+    mlir::Value rhsTempExpr;
+    if (rhsNeedsTemporary) {
+      rhsTempExpr = hlfir::AsExprOp::create(builder, loc, rhs);
+      rhs = hlfir::Entity{rhsTempExpr};
+    }
+
     // 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
@@ -138,13 +181,11 @@ 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);
+
+    if (rhsTempExpr)
+      hlfir::DestroyOp::create(builder, loc, rhsTempExpr);
+
     rewriter.eraseOp(assign);
     return mlir::success();
   }
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..6e5cce3d5d8b0
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
@@ -0,0 +1,102 @@
+// Self-copy of array sections with the same runtime stride on LHS and RHS.
+// Runtime stride prevents genRuntimeDisjointnessCheck(..) from building a disjoint
+// i1, so inlining uses hlfir.as_expr temporary
+//
+// 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>>
+
+// Box self-assign is lowered with a temp expr (no runtime disjointness guard).
+// CHECK: hlfir.designate
+// CHECK: hlfir.designate
+// CHECK: [[EXPR:%.*]] = hlfir.as_expr
+// CHECK-NOT: fir.if
+// CHECK: fir.do_loop
+// CHECK: hlfir.apply [[EXPR]]
+// CHECK: hlfir.assign
+// CHECK: hlfir.destroy [[EXPR]]
+
+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..84c3071c43dd7
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
@@ -0,0 +1,148 @@
+// 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"
+// CHECK-DAG: hlfir.designate %{{.*}}#0 (
+// CHECK-DAG: hlfir.designate %{{.*}}#0 (
+// CHECK: fir.if %{{.*}} {
+// CHECK: fir.do_loop
+// CHECK: hlfir.assign
+// CHECK: } else {
+// CHECK: hlfir.as_expr
+// CHECK: fir.do_loop
+// CHECK: hlfir.destroy
+
+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
+}

>From 6f2f6b4f528208868f9a7bbb5a684d2645926e72 Mon Sep 17 00:00:00 2001
From: Kaviya Rajendiran <kaviyara2000 at gmail.com>
Date: Tue, 23 Jun 2026 21:58:43 +0530
Subject: [PATCH 2/2] [Flang] Fall back to address-based overlap check when
 index-based check is inapplicable

---
 .../Optimizer/Analysis/ArraySectionAnalyzer.h |  17 +--
 .../flang/Optimizer/Builder/FIRBuilder.h      |  10 ++
 .../Analysis/ArraySectionAnalyzer.cpp         |  51 -------
 flang/lib/Optimizer/Builder/FIRBuilder.cpp    | 137 ++++++++++++++++++
 .../HLFIR/Transforms/InlineHLFIRAssign.cpp    |  57 ++++----
 .../inline-hlfir-assign-pointer-overlap.fir   | 132 +++++++++++++++++
 ...-hlfir-assign-self-copy-runtime-stride.fir |  65 +++++++--
 .../HLFIR/inline-hlfir-assign-self-copy.fir   |  46 +++++-
 8 files changed, 398 insertions(+), 117 deletions(-)
 create mode 100644 flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir

diff --git a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
index 5bf60c49621ab..aae9cbde2d7be 100644
--- a/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
+++ b/flang/include/flang/Optimizer/Analysis/ArraySectionAnalyzer.h
@@ -9,13 +9,13 @@
 #ifndef FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
 #define FORTRAN_OPTIMIZER_ANALYSIS_ARRAYSECTIONANALYZER_H
 
-#include "mlir/IR/Location.h"
 #include "mlir/IR/Operation.h"
 #include "mlir/IR/Value.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 
 namespace mlir {
-class OpBuilder;
+class Operation;
+class Value;
 } // namespace mlir
 
 namespace hlfir {
@@ -62,21 +62,9 @@ class ArraySectionAnalyzer {
   analyze(mlir::Value ref1, mlir::Value ref2,
           ValueEquivalenceCallback areKnownEquivalent = nullptr);
 
-  ///  Generate a runtime disjointness check for the given LHS and RHS.
-  ///  Check if the sections are disjoint using `ub1 < lb2 || ub2 < lb1`
-  ///  Returns true if the sections are disjoint, false otherwise.
-  ///  Returns nullptr if the operands are not a matching pair of
-  ///  hlfir.designate on the same base or no dimension has statically-known
-  ///  stride order.
-  static mlir::Value genRuntimeDisjointnessCheck(mlir::Location loc,
-                                                 mlir::OpBuilder &builder,
-                                                 mlir::Value lhsRef,
-                                                 mlir::Value rhsRef);
-
   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
@@ -114,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 a41be5efacb56..5a708e5fcd393 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -988,6 +988,16 @@ mlir::Value getDescriptorWithNewBaseAddress(fir::FirOpBuilder &builder,
                                             mlir::Location loc, mlir::Value box,
                                             mlir::Value newAddr);
 
+/// Generate a index-based runtime disjointness check.
+std::optional<mlir::Value>
+genIndexBasedDisjointnessCheck(mlir::Location loc, fir::FirOpBuilder &builder,
+                               mlir::Value lhsRef, mlir::Value rhsRef);
+
+/// Generate a address-based runtime 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/Analysis/ArraySectionAnalyzer.cpp b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
index f657d67289874..6eec184b99a7f 100644
--- a/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
+++ b/flang/lib/Optimizer/Analysis/ArraySectionAnalyzer.cpp
@@ -11,7 +11,6 @@
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
-#include "mlir/IR/BuiltinTypes.h"
 #include "llvm/Support/Debug.h"
 
 #define DEBUG_TYPE "array-section-analyzer"
@@ -194,56 +193,6 @@ ArraySectionAnalyzer::analyze(mlir::Value ref1, mlir::Value ref2,
   return SlicesOverlapKind::Unknown;
 }
 
-mlir::Value ArraySectionAnalyzer::genRuntimeDisjointnessCheck(
-    mlir::Location loc, mlir::OpBuilder &builder, mlir::Value lhsRef,
-    mlir::Value rhsRef) {
-  auto des1 = lhsRef.getDefiningOp<hlfir::DesignateOp>();
-  auto des2 = rhsRef.getDefiningOp<hlfir::DesignateOp>();
-  if (!des1 || !des2)
-    return {};
-
-  if (des1.getMemref() != des2.getMemref())
-    return {};
-
-  if (des1.getComponent() != des2.getComponent() ||
-      des1.getComponentShape() != des2.getComponentShape() ||
-      des1.getSubstring() != des2.getSubstring() ||
-      des1.getComplexPart() != des2.getComplexPart() ||
-      des1.getTypeparams() != des2.getTypeparams())
-    return {};
-
-  if (des1.getIsTriplet().empty() ||
-      !llvm::equal(des1.getIsTriplet(), des2.getIsTriplet()))
-    return {};
-
-  mlir::Type idxTy = mlir::IndexType::get(des1.getContext());
-  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()) {
-    SectionDesc desc1 = readSectionDesc(des1It, isTriplet);
-    SectionDesc desc2 = readSectionDesc(des2It, isTriplet);
-    auto [lb1, ub1] = getOrderedBounds(desc1);
-    auto [lb2, ub2] = 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;
-  }
-  return disjoint;
-}
-
 bool ArraySectionAnalyzer::isLess(mlir::Value v1, mlir::Value v2) {
   auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
     auto *op = v.getDefiningOp();
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index c7df3abda156c..5cbc9fcedc9b5 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"
@@ -2006,3 +2009,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 55c67c6773553..e6dac7e7cabf8 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/InlineHLFIRAssign.cpp
@@ -120,13 +120,12 @@ class InlineHLFIRAssignConversion
                                   << "\tRHS: " << rhs << "\n"
                                   << "\tALIAS: " << aliasRes << "\n");
           // Overlap is Unknown: unsafe to read RHS while writing LHS
-          // without a temp. Call
-          // ArraySectionAnalyzer::genRuntimeDisjointnessCheck to check if the
-          // sections are disjoint.
-          // 1. Sections disjoint at runtime-> direct element-wise copy (no
-          // temp)
-          // 2. Sections are not disjoint at runtime  -> Allocate a temporary
-          // and copy RHS into it, then copy the temporary to 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;
         }
       }
@@ -149,28 +148,27 @@ class InlineHLFIRAssignConversion
     };
 
     if (rhsNeedsTemporary) {
-      if (mlir::Value disjoint =
-              fir::ArraySectionAnalyzer::genRuntimeDisjointnessCheck(
-                  loc, builder, lhs, rhs)) {
-        builder.genIfThenElse(loc, disjoint)
-            .genThen([&]() { emitAssignFrom(rhs); })
-            .genElse([&]() {
-              mlir::Value tempExpr = hlfir::AsExprOp::create(builder, loc, rhs);
-              emitAssignFrom(hlfir::Entity{tempExpr});
-              hlfir::DestroyOp::create(builder, loc, tempExpr);
-            })
-            .end();
-        rewriter.eraseOp(assign);
-        return mlir::success();
+      std::optional<mlir::Value> disjoint =
+          fir::factory::genIndexBasedDisjointnessCheck(loc, builder, lhs, rhs);
+      if (!disjoint) {
+        disjoint = fir::factory::genAddressBasedDisjointnessCheck(loc, builder,
+                                                                  lhs, rhs);
       }
-    }
-
-    // When rhsNeedsTemporary is true and genRuntimeDisjointnessCheck
-    // returned null, always use a temporary rhsExpr.
-    mlir::Value rhsTempExpr;
-    if (rhsNeedsTemporary) {
-      rhsTempExpr = hlfir::AsExprOp::create(builder, loc, rhs);
-      rhs = hlfir::Entity{rhsTempExpr};
+      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([&]() {
+            mlir::Value tempExpr = hlfir::AsExprOp::create(builder, loc, rhs);
+            emitAssignFrom(hlfir::Entity{tempExpr});
+            hlfir::DestroyOp::create(builder, loc, tempExpr);
+          })
+          .end();
+      rewriter.eraseOp(assign);
+      return mlir::success();
     }
 
     // Materialize scalar RHS before the assignment loop. Fortran 10.2.1.3
@@ -183,9 +181,6 @@ class InlineHLFIRAssignConversion
 
     emitAssignFrom(rhs);
 
-    if (rhsTempExpr)
-      hlfir::DestroyOp::create(builder, loc, rhsTempExpr);
-
     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..cb3fa5334e568
--- /dev/null
+++ b/flang/test/HLFIR/inline-hlfir-assign-pointer-overlap.fir
@@ -0,0 +1,132 @@
+// 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:   %[[EXPR:.*]] = hlfir.as_expr %{{.*}}
+// CHECK:   fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK:     hlfir.apply %[[EXPR]], %{{.*}}
+// CHECK:     hlfir.designate
+// CHECK:     hlfir.assign %{{.*}} to %{{.*}} : i32, !fir.ref<i32>
+// CHECK:   }
+// CHECK:   hlfir.destroy %[[EXPR]]
+// 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-self-copy-runtime-stride.fir b/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
index 6e5cce3d5d8b0..ee53844b010a8 100644
--- a/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy-runtime-stride.fir
@@ -1,7 +1,4 @@
-// Self-copy of array sections with the same runtime stride on LHS and RHS.
-// Runtime stride prevents genRuntimeDisjointnessCheck(..) from building a disjoint
-// i1, so inlining uses hlfir.as_expr temporary
-//
+//  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:
@@ -21,15 +18,57 @@
 // CHECK-LABEL: func.func @_QPtestcase
 // CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.array<?xf32>>
 
-// Box self-assign is lowered with a temp expr (no runtime disjointness guard).
-// CHECK: hlfir.designate
-// CHECK: hlfir.designate
-// CHECK: [[EXPR:%.*]] = hlfir.as_expr
-// CHECK-NOT: fir.if
-// CHECK: fir.do_loop
-// CHECK: hlfir.apply [[EXPR]]
-// CHECK: hlfir.assign
-// CHECK: hlfir.destroy [[EXPR]]
+// 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:   %[[EXPR:.*]] = hlfir.as_expr %{{.*}}
+// CHECK:   fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK:     %{{.*}} = hlfir.apply %[[EXPR]], %{{.*}}
+// CHECK:     %{{.*}} = hlfir.designate %{{.*}} (%{{.*}})
+// CHECK:     hlfir.assign %{{.*}} to %{{.*}}
+// CHECK:   }
+// CHECK:   hlfir.destroy %[[EXPR]]
+// 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
diff --git a/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir b/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
index 84c3071c43dd7..ba1d278080a02 100644
--- a/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
+++ b/flang/test/HLFIR/inline-hlfir-assign-self-copy.fir
@@ -18,15 +18,45 @@
 // CHECK-SAME: %[[ARGF:.*]]: !fir.ref<!fir.array<?x?x?xf32>> {fir.bindc_name = "field"}
 
 // CHECK: hlfir.declare %[[ARGF]](%{{.*}}) {{.*}} uniq_name = "_QFself_copy_ewEfield"
-// CHECK-DAG: hlfir.designate %{{.*}}#0 (
-// CHECK-DAG: hlfir.designate %{{.*}}#0 (
-// CHECK: fir.if %{{.*}} {
-// CHECK: fir.do_loop
-// CHECK: hlfir.assign
+
+// 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: hlfir.as_expr
-// CHECK: fir.do_loop
-// CHECK: hlfir.destroy
+// CHECK:   %[[EXPR:.*]] = hlfir.as_expr %[[RHS]]
+// CHECK:   fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK:     fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK:       fir.do_loop %{{.*}} = %{{.*}} to %{{.*}} step %{{.*}} unordered {
+// CHECK:         %{{.*}} = hlfir.apply %[[EXPR]], %{{.*}}, %{{.*}}, %{{.*}}
+// CHECK:         %{{.*}} = hlfir.designate %[[LHS]] (%{{.*}}, %{{.*}}, %{{.*}})
+// CHECK:         hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+// CHECK:       }
+// CHECK:     }
+// CHECK:   }
+// CHECK:   hlfir.destroy %[[EXPR]]
+// 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



More information about the flang-commits mailing list