[flang-commits] [flang] [flang] Preserve DO variable updates through aliases (PR #211191)

via flang-commits flang-commits at lists.llvm.org
Wed Jul 22 00:47:33 PDT 2026


https://github.com/khaki3 created https://github.com/llvm/llvm-project/pull/211191

Use FIR ModRef analysis to retain memory-driven induction when a loop body may update the DO variable through another reference.

>From 1521ee605093534b6211f5112716877df357d57a Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Wed, 22 Jul 2026 00:43:50 -0700
Subject: [PATCH] [flang] Preserve DO variable updates through aliases

Use FIR ModRef analysis to retain memory-driven induction when a loop body may update the DO variable through another reference.
---
 flang/lib/Lower/Bridge.cpp                | 50 ++++++++++++++++-----
 flang/lib/Lower/CMakeLists.txt            |  2 +
 flang/test/Lower/do_loop_alias_update.f90 | 53 +++++++++++++++++++++++
 3 files changed, 93 insertions(+), 12 deletions(-)
 create mode 100644 flang/test/Lower/do_loop_alias_update.f90

diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 7cde3ae492e14..f34d9c5f06008 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -32,6 +32,7 @@
 #include "flang/Lower/StatementContext.h"
 #include "flang/Lower/Support/ReductionProcessor.h"
 #include "flang/Lower/Support/Utils.h"
+#include "flang/Optimizer/Analysis/AliasAnalysis.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
 #include "flang/Optimizer/Builder/CUFCommon.h"
 #include "flang/Optimizer/Builder/Character.h"
@@ -132,6 +133,7 @@ struct IncrementLoopInfo {
 
   // Data members for structured loops.
   mlir::Operation *loopOp = nullptr;
+  mlir::Operation *loopVariableStore = nullptr;
 
   // Data members for unstructured loops.
   bool hasRealControl = false;
@@ -3062,18 +3064,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
         if (genDoConcurrent)
           continue;
 
-        // Lower the loop without the secondary-induction iter_arg so memory
-        // recurrences (e.g. reductions) stay visible to later analyses. The DO
-        // variable is recomputed from the induction variable in the body; its
-        // post-loop value is materialized in genFIRIncrementLoopEnd.
+        // Start with SSA-controlled induction. After lowering the body, switch
+        // to memory-controlled induction if another reference may modify it.
         auto loopOp = fir::DoLoopOp::create(
             *builder, loc, lowerValue, upperValue, stepValue,
             /*unordered=*/false, /*finalCountValue=*/false,
             /*iterArgs=*/mlir::ValueRange{});
         info.loopOp = loopOp;
         builder->setInsertionPointToStart(loopOp.getBody());
-        fir::StoreOp::create(*builder, loc, loopOp.getInductionVar(),
-                             info.loopVariable);
+        info.loopVariableStore = fir::StoreOp::create(
+            *builder, loc, loopOp.getInductionVar(), info.loopVariable);
         addLoopAnnotationAttr(info, dirs);
         continue;
       }
@@ -3214,13 +3214,39 @@ class FirConverter : public Fortran::lower::AbstractConverter {
           continue;
         }
 
-        // End fir.do_loop. The loop carries no secondary-induction iter_arg, so
-        // materialize the Fortran post-loop value lb + tripCount*step after the
-        // loop for later uses of the DO variable. Compute it in the loop's
-        // index type (matching how the loop counts iterations) so the trip
-        // arithmetic does not overflow the DO variable's type for empty
-        // range-extreme loops.
+        // Detect writes that may update the DO variable through another
+        // reference.
         auto doLoopOp = mlir::cast<fir::DoLoopOp>(info.loopOp);
+        fir::AliasAnalysis aliasAnalysis;
+        bool loopVariableMayBeModified = false;
+        for (mlir::Operation &op : doLoopOp.getBody()->without_terminator()) {
+          if (&op != info.loopVariableStore &&
+              aliasAnalysis.getModRef(&op, info.loopVariable).isMod()) {
+            loopVariableMayBeModified = true;
+            break;
+          }
+        }
+        if (loopVariableMayBeModified) {
+          builder->setInsertionPoint(doLoopOp);
+          fir::StoreOp::create(*builder, loc, doLoopOp.getLowerBound(),
+                               info.loopVariable);
+          info.loopVariableStore->erase();
+          info.loopVariableStore = nullptr;
+
+          builder->setInsertionPoint(doLoopOp.getBody()->getTerminator());
+          mlir::Value value =
+              fir::LoadOp::create(*builder, loc, info.loopVariable);
+          mlir::Value step = builder->createConvert(
+              loc, info.getLoopVariableType(), doLoopOp.getStep());
+          value =
+              mlir::arith::AddIOp::create(*builder, loc, value, step, iofAttr);
+          fir::StoreOp::create(*builder, loc, value, info.loopVariable);
+          builder->setInsertionPointAfter(doLoopOp);
+          continue;
+        }
+
+        // Compute the SSA-controlled post-loop value in index type to avoid
+        // narrow integer overflow for empty range-extreme loops.
         builder->setInsertionPointAfter(doLoopOp);
         mlir::Type idxTy = builder->getIndexType();
         mlir::Value lb =
diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index 2db97f4f7ec45..d90e98953a9f0 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -53,6 +53,7 @@ add_flang_library(FortranLower
   DEPENDS
   CUFAttrs
   CUFDialect
+  FIRAnalysis
   FIRDialect
   FIRTransforms
   HLFIRDialect
@@ -61,6 +62,7 @@ add_flang_library(FortranLower
   LINK_LIBS
   CUFAttrs
   CUFDialect
+  FIRAnalysis
   FIRDialect
   FIRDialectSupport
   FIRBuilder
diff --git a/flang/test/Lower/do_loop_alias_update.f90 b/flang/test/Lower/do_loop_alias_update.f90
new file mode 100644
index 0000000000000..56fb2461c3016
--- /dev/null
+++ b/flang/test/Lower/do_loop_alias_update.f90
@@ -0,0 +1,53 @@
+! RUN: %flang_fc1 -emit-hlfir -mmlir --unsafe-cray-pointers -o - %s | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPcray_pointer_loop()
+subroutine cray_pointer_loop
+  integer :: g, g5
+  integer(8) :: lm
+  pointer(lm, gpl6l)
+
+  ! CHECK: %[[G_REF:.*]] = fir.alloca i32 {bindc_name = "g"
+  ! CHECK: %[[G:.*]]:2 = hlfir.declare %[[G_REF]]
+  lm = loc(g)
+  g5 = 0
+
+  ! CHECK: %[[LB:.*]] = arith.constant -2 : i32
+  ! CHECK: %[[UB:.*]] = arith.constant -7 : i32
+  ! CHECK: %[[STEP:.*]] = arith.constant -2 : i32
+  ! CHECK: fir.store %[[LB]] to %[[G]]#0 : !fir.ref<i32>
+  ! CHECK: fir.do_loop %[[IV:.*]] = %[[LB]] to %[[UB]] step %[[STEP]] : i32 {
+  ! CHECK-NOT: fir.store %[[IV]] to %[[G]]#0
+  do g = -2, -7, -2
+    g5 = g5 + 5
+    gpl6l = g - 3
+  ! CHECK: %[[UPDATED:.*]] = fir.load %[[G]]#0 : !fir.ref<i32>
+  ! CHECK: %[[NEXT:.*]] = arith.addi %[[UPDATED]], %[[STEP]] overflow<nsw> : i32
+  ! CHECK: fir.store %[[NEXT]] to %[[G]]#0 : !fir.ref<i32>
+  ! CHECK: fir.result
+  ! CHECK: }
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPpointer_alias_loop()
+subroutine pointer_alias_loop
+  integer, target :: i
+  integer, pointer :: p
+
+  p => i
+  ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i"
+  ! CHECK: %[[I:.*]]:2 = hlfir.declare %[[I_REF]]
+  ! CHECK: %[[LB:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[UB:.*]] = arith.constant 3 : i32
+  ! CHECK: %[[STEP:.*]] = arith.constant 1 : i32
+  ! CHECK: fir.store %[[LB]] to %[[I]]#0 : !fir.ref<i32>
+  ! CHECK: fir.do_loop %[[IV:.*]] = %[[LB]] to %[[UB]] step %[[STEP]] : i32 {
+  ! CHECK-NOT: fir.store %[[IV]] to %[[I]]#0
+  do i = 1, 3
+    p = i + 1
+  ! CHECK: %[[UPDATED:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
+  ! CHECK: %[[NEXT:.*]] = arith.addi %[[UPDATED]], %[[STEP]] overflow<nsw> : i32
+  ! CHECK: fir.store %[[NEXT]] to %[[I]]#0 : !fir.ref<i32>
+  ! CHECK: fir.result
+  ! CHECK: }
+  end do
+end subroutine



More information about the flang-commits mailing list