[flang-commits] [flang] [flang][FIRToSCF] Recompute a typed induction variable in closed form (PR #217051)

via flang-commits flang-commits at lists.llvm.org
Tue Aug 18 10:28:20 PDT 2026


https://github.com/khaki3 updated https://github.com/llvm/llvm-project/pull/217051

>From 5e25e9a33982b9176f0682733526bc81abd96a59 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 17 Aug 2026 16:16:03 -0700
Subject: [PATCH 1/3] [flang][FIRToSCF] Recompute a typed induction variable in
 closed form

A fir.do_loop whose induction variable is not of index type was converted
into a normalized scf.for that carried the induction variable as an
iter_arg. That gives the loop a result which loop-level analyses, such as
reduction recognition, cannot look past, even though the value is only the
induction variable restated.

Recompute the original induction variable from the canonical one inside the
body instead, the way the index case already did. No iter_arg and no loop
result are added, for positive, negative and non-constant steps alike. The
closed form does not inherit nsw, since it can wrap where the step-by-step
increment does not.

Loops with a final value still carry the induction variable, as their result
is genuinely observable after the loop.
---
 flang/lib/Optimizer/Transforms/FIRToSCF.cpp | 38 ++++++-----
 flang/test/Fir/FirToSCF/do-loop.fir         | 72 ++++++++++++++++++++-
 2 files changed, 90 insertions(+), 20 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
index d1ea97cac1e88..d19e443eef7ba 100644
--- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
@@ -53,8 +53,12 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
     assert(low && high && "must be a Value");
     mlir::Value step = doLoopOp.getStep();
     bool hasTypedIV = !low.getType().isIndex();
+    // A typed (non-index) induction variable does not have to be loop-carried:
+    // it is recomputed below in closed form from the canonical induction
+    // variable. Carrying it would give the loop an extra result that
+    // loop-level analyses (e.g. reduction recognition) cannot look past.
     mlir::SmallVector<mlir::Value> iterArgs;
-    if (hasTypedIV || hasFinalValue)
+    if (hasFinalValue)
       iterArgs.push_back(low);
     iterArgs.append(doLoopOp.getIterOperands().begin(),
                     doLoopOp.getIterOperands().end());
@@ -104,18 +108,23 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
 
     rewriter.setInsertionPointToStart(&scfLoopBody);
     mlir::Value iv;
-    if (hasTypedIV) {
+    if (hasTypedIV && hasFinalValue) {
       iv = scfLoopLikeOp.getRegionIterArgs().front();
     } else {
-      iv = mlir::arith::MulIOp::create(
-          rewriter, loc, scfLoopLikeOp.getSingleInductionVar().value(), step);
+      mlir::Value canonicalIV = scfLoopLikeOp.getSingleInductionVar().value();
+      // Recompute the original induction variable in its own type. No nsw here:
+      // the closed form can wrap where the step-by-step increment does not.
+      if (hasTypedIV)
+        canonicalIV =
+            fir::ConvertOp::create(rewriter, loc, low.getType(), canonicalIV);
+      iv = mlir::arith::MulIOp::create(rewriter, loc, canonicalIV, step);
       iv = mlir::arith::AddIOp::create(rewriter, loc, low, iv);
     }
     mlir::Value firIV = doLoopOp.getInductionVar();
     firIV.replaceAllUsesWith(iv);
 
     mlir::Value finalValue;
-    if (hasTypedIV) {
+    if (hasTypedIV && hasFinalValue) {
       finalValue =
           mlir::arith::AddIOp::create(rewriter, loc, iv, step, iofAttr);
     } else if (hasFinalValue) {
@@ -134,23 +143,21 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
             mlir::arith::AddIOp::create(rewriter, loc, iv, step, iofAttr);
     }
 
-    if (hasTypedIV || hasFinalValue || !results.empty()) {
+    if (hasFinalValue || !results.empty()) {
       rewriter.setInsertionPointToEnd(&scfLoopBody);
       llvm::SmallVector<mlir::Value> yieldOperands;
-      if (hasTypedIV || hasFinalValue) {
+      if (hasFinalValue) {
         yieldOperands.push_back(finalValue);
-      }
-      if (hasFinalValue)
         llvm::append_range(yieldOperands, results.drop_front());
-      else
+      } else {
         llvm::append_range(yieldOperands, results);
+      }
       mlir::scf::YieldOp::create(rewriter, resultOp->getLoc(), yieldOperands);
     }
     rewriter.replaceAllUsesWith(
         doLoopOp.getRegionIterArgs(),
-        hasTypedIV || hasFinalValue
-            ? scfLoopLikeOp.getRegionIterArgs().drop_front()
-            : scfLoopLikeOp.getRegionIterArgs());
+        hasFinalValue ? scfLoopLikeOp.getRegionIterArgs().drop_front()
+                      : scfLoopLikeOp.getRegionIterArgs());
 
     // Copy loop annotations from the fir.do_loop to scf loop op.
     if (auto ann = doLoopOp.getLoopAnnotation())
@@ -161,10 +168,7 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
     if (auto parDims = doLoopOp->getAttr(mlir::acc::GPUParallelDimsAttr::name))
       scfLoopOp->setAttr(mlir::acc::GPUParallelDimsAttr::name, parDims);
 
-    mlir::ValueRange scfResults = scfLoopOp->getResults();
-    if (hasTypedIV && !hasFinalValue)
-      scfResults = scfResults.drop_front();
-    rewriter.replaceOp(doLoopOp, scfResults);
+    rewriter.replaceOp(doLoopOp, scfLoopOp->getResults());
     return mlir::success();
   }
 
diff --git a/flang/test/Fir/FirToSCF/do-loop.fir b/flang/test/Fir/FirToSCF/do-loop.fir
index b13fa033b118a..eba01c5893596 100644
--- a/flang/test/Fir/FirToSCF/do-loop.fir
+++ b/flang/test/Fir/FirToSCF/do-loop.fir
@@ -34,6 +34,8 @@ func.func @simple_loop(%arg0: !fir.ref<!fir.array<100xi32>>) {
 
 // -----
 
+// A typed induction variable is recomputed in closed form from the canonical
+// induction variable, so it adds neither an iter_arg nor a loop result.
 // CHECK-LABEL: func.func @typed_loop(
 // CHECK-SAME: %[[LB:.*]]: i32, %[[UB:.*]]: i32, %[[STEP:.*]]: i32,
 // CHECK-SAME: %[[ADDR:.*]]: !fir.ref<i32>) {
@@ -45,11 +47,13 @@ func.func @simple_loop(%arg0: !fir.ref<!fir.array<100xi32>>) {
 // CHECK: %[[TRIP:.*]] = arith.divsi %[[DISTANCE]], %[[STEP_IDX]] : index
 // CHECK: %[[C0:.*]] = arith.constant 0 : index
 // CHECK: %[[C1:.*]] = arith.constant 1 : index
-// CHECK: scf.for %{{.*}} = %[[C0]] to %[[TRIP]] step %[[C1]] iter_args(%[[IV:.*]] = %[[LB]]) -> (i32) {
-// CHECK:   %[[NEXT:.*]] = arith.addi %[[IV]], %[[STEP]] overflow<nsw> : i32
+// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[TRIP]] step %[[C1]] {
+// CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[STEP]] : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[LB]], %[[SCALED]] : i32
 // CHECK:   fir.store %[[IV]] to %[[ADDR]] : !fir.ref<i32>
-// CHECK:   scf.yield %[[NEXT]] : i32
 // CHECK: }
+// CHECK-NOT: iter_args
 func.func @typed_loop(%lb: i32, %ub: i32, %step: i32,
                       %addr: !fir.ref<i32>) {
   fir.do_loop %iv = %lb to %ub step %step : i32 {
@@ -60,6 +64,68 @@ func.func @typed_loop(%lb: i32, %ub: i32, %step: i32,
 
 // -----
 
+// Only the loop's own iter_args survive; the typed IV does not add one.
+// CHECK-LABEL: func.func @typed_loop_iter_args(
+// CHECK: %[[C0:.*]] = arith.constant 0 : i32
+// CHECK: %[[C1:.*]] = arith.constant 1 : i32
+// CHECK: %[[TRIP:.*]] = arith.divsi
+// CHECK: %[[C0_IDX:.*]] = arith.constant 0 : index
+// CHECK: %[[C1_IDX:.*]] = arith.constant 1 : index
+// CHECK: %[[RES:.*]] = scf.for %[[I:.*]] = %[[C0_IDX]] to %[[TRIP]] step %[[C1_IDX]] iter_args(%[[ACC:.*]] = %[[C0]]) -> (i32) {
+// CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[C1]] : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[C1]], %[[SCALED]] : i32
+// CHECK:   %[[SUM:.*]] = arith.addi %[[ACC]], %[[IV]] : i32
+// CHECK:   scf.yield %[[SUM]] : i32
+// CHECK: }
+// CHECK: return %[[RES]] : i32
+func.func @typed_loop_iter_args(%addr: !fir.ref<i32>) -> i32 {
+  %c0_i32 = arith.constant 0 : i32
+  %c1_i32 = arith.constant 1 : i32
+  %c10_i32 = arith.constant 10 : i32
+  %r = fir.do_loop %iv = %c1_i32 to %c10_i32 step %c1_i32 iter_args(%acc = %c0_i32) -> (i32) : i32 {
+    %v = arith.addi %acc, %iv : i32
+    fir.result %v : i32
+  }
+  return %r : i32
+}
+
+// -----
+
+// A negative step is handled by the same closed form, so it does not need an
+// iter_arg either.
+// CHECK-LABEL: func.func @typed_loop_negative_step(
+// CHECK-SAME: %[[ADDR:.*]]: !fir.ref<i32>) {
+// CHECK: %[[C1:.*]] = arith.constant 1 : i32
+// CHECK: %[[C10:.*]] = arith.constant 10 : i32
+// CHECK: %[[CM1:.*]] = arith.constant -1 : i32
+// CHECK: %[[LB_IDX:.*]] = fir.convert %[[C10]] : (i32) -> index
+// CHECK: %[[UB_IDX:.*]] = fir.convert %[[C1]] : (i32) -> index
+// CHECK: %[[STEP_IDX:.*]] = fir.convert %[[CM1]] : (i32) -> index
+// CHECK: %[[DIFF:.*]] = arith.subi %[[UB_IDX]], %[[LB_IDX]] : index
+// CHECK: %[[DISTANCE:.*]] = arith.addi %[[DIFF]], %[[STEP_IDX]] : index
+// CHECK: %[[TRIP:.*]] = arith.divsi %[[DISTANCE]], %[[STEP_IDX]] : index
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK: %[[C1_IDX:.*]] = arith.constant 1 : index
+// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[TRIP]] step %[[C1_IDX]] {
+// CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[CM1]] : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[C10]], %[[SCALED]] : i32
+// CHECK:   fir.store %[[IV]] to %[[ADDR]] : !fir.ref<i32>
+// CHECK: }
+// CHECK-NOT: iter_args
+func.func @typed_loop_negative_step(%addr: !fir.ref<i32>) {
+  %c1_i32 = arith.constant 1 : i32
+  %c10_i32 = arith.constant 10 : i32
+  %cm1_i32 = arith.constant -1 : i32
+  fir.do_loop %iv = %c10_i32 to %c1_i32 step %cm1_i32 : i32 {
+    fir.store %iv to %addr : !fir.ref<i32>
+  }
+  return
+}
+
+// -----
+
 // CHECK-LABEL:   func.func @loop_with_negtive_step(
 // CHECK-SAME:      %[[ARG0:.*]]: !fir.ref<!fir.array<100xi32>>) {
 // CHECK:           %[[VAL_0:.*]] = arith.constant 100 : index

>From e1d0021b390c46359174828af76ff3a036a56d0f Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Tue, 18 Aug 2026 07:23:45 -0700
Subject: [PATCH 2/3] [NFC] Trim the closed-form induction variable comments

Keep only the nsw note, which the code cannot state on its own. The
rationale for dropping the iter_arg belongs in the commit message.
---
 flang/lib/Optimizer/Transforms/FIRToSCF.cpp | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
index d19e443eef7ba..b381a14ec37a9 100644
--- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
@@ -53,10 +53,6 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
     assert(low && high && "must be a Value");
     mlir::Value step = doLoopOp.getStep();
     bool hasTypedIV = !low.getType().isIndex();
-    // A typed (non-index) induction variable does not have to be loop-carried:
-    // it is recomputed below in closed form from the canonical induction
-    // variable. Carrying it would give the loop an extra result that
-    // loop-level analyses (e.g. reduction recognition) cannot look past.
     mlir::SmallVector<mlir::Value> iterArgs;
     if (hasFinalValue)
       iterArgs.push_back(low);
@@ -112,11 +108,10 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
       iv = scfLoopLikeOp.getRegionIterArgs().front();
     } else {
       mlir::Value canonicalIV = scfLoopLikeOp.getSingleInductionVar().value();
-      // Recompute the original induction variable in its own type. No nsw here:
-      // the closed form can wrap where the step-by-step increment does not.
       if (hasTypedIV)
         canonicalIV =
             fir::ConvertOp::create(rewriter, loc, low.getType(), canonicalIV);
+      // No nsw: the closed form can wrap where an increment by step does not.
       iv = mlir::arith::MulIOp::create(rewriter, loc, canonicalIV, step);
       iv = mlir::arith::AddIOp::create(rewriter, loc, low, iv);
     }

>From 9bc9a35316247d1081a45493bccb8e4cc4a32026 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Tue, 18 Aug 2026 10:27:50 -0700
Subject: [PATCH 3/3] [flang][FIRToSCF] Preserve nsw on the closed-form typed
 IV recompute

The typed induction variable is now recomputed in closed form
(low + canonicalIV * step) instead of carried as an iter_arg, but the
muli/addi that do the recompute were emitted without the pass's
set-nsw flag, unlike the stepped increment they replace. Without nsw,
SCEV can't fold the narrow IV's sign-extension into an affine
recurrence, which silently regresses the vectorization that
set-nsw (#211796) restored for variable-step loops.

Pass the existing iofAttr to both ops so they carry the same no-wrap
assumption as the increment they replace.
---
 flang/lib/Optimizer/Transforms/FIRToSCF.cpp |  8 ++--
 flang/test/Fir/FirToSCF/do-extra.fir        |  6 +--
 flang/test/Fir/FirToSCF/do-loop.fir         | 44 ++++++++++-----------
 flang/test/Fir/FirToSCF/normalize.fir       |  8 ++--
 4 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
index b381a14ec37a9..a569a717fdca4 100644
--- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
@@ -111,9 +111,11 @@ struct DoLoopConversion : public mlir::OpRewritePattern<fir::DoLoopOp> {
       if (hasTypedIV)
         canonicalIV =
             fir::ConvertOp::create(rewriter, loc, low.getType(), canonicalIV);
-      // No nsw: the closed form can wrap where an increment by step does not.
-      iv = mlir::arith::MulIOp::create(rewriter, loc, canonicalIV, step);
-      iv = mlir::arith::AddIOp::create(rewriter, loc, low, iv);
+      // Keep the no-wrap flags the stepped increment carried, so a narrow IV
+      // still folds into an affine recurrence.
+      iv = mlir::arith::MulIOp::create(rewriter, loc, canonicalIV, step,
+                                       iofAttr);
+      iv = mlir::arith::AddIOp::create(rewriter, loc, low, iv, iofAttr);
     }
     mlir::Value firIV = doLoopOp.getInductionVar();
     firIV.replaceAllUsesWith(iv);
diff --git a/flang/test/Fir/FirToSCF/do-extra.fir b/flang/test/Fir/FirToSCF/do-extra.fir
index 2d473e07a9407..1a1d1b880798f 100644
--- a/flang/test/Fir/FirToSCF/do-extra.fir
+++ b/flang/test/Fir/FirToSCF/do-extra.fir
@@ -72,7 +72,7 @@ func.func @implied_do_in_acc_serial(%arg0: !fir.ref<!fir.array<4xi32>> {fir.bind
 // CHECK:       [[ADD:%[0-9]+]] = arith.addi [[SUB]], %c1 : index
 // CHECK:       [[DIV:%[0-9]+]] = arith.divsi [[ADD]], %c1 : index
 // CHECK:       [[FOR:%[0-9]+]] = scf.for %{{.*}} = %{{.*}} to [[DIV]] step %{{.*}} iter_args(%{{.*}} = %c1) -> (index) {
-// CHECK:       [[IV:%[0-9]+]] = arith.addi %c1, {{.*}} : index
+// CHECK:       [[IV:%[0-9]+]] = arith.addi %c1, {{.*}} overflow<nsw> : index
 // CHECK:       fir.convert [[IV]] : (index) -> i32
 // CHECK:       scf.yield
 
@@ -135,8 +135,8 @@ func.func @mv_(%arg0: !fir.ref<!fir.array<3xi32>> {fir.bindc_name = "a", llvm.no
 // CHECK:           %[[C0:.*]] = arith.constant 0 : index
 // CHECK:           %[[C1:.*]] = arith.constant 1 : index
 // CHECK:           scf.for %{{.*}} = %[[C0]] to %[[VAL_2]] step %[[C1]] iter_args(%{{.*}} = %[[ARG0]]) -> (index) {
-// CHECK:             %[[MUL:.*]] = arith.muli %{{.*}}, %[[ARG2]] : index
-// CHECK:             %[[ADD0:.*]] = arith.addi %[[ARG0]], %[[MUL]] : index
+// CHECK:             %[[MUL:.*]] = arith.muli %{{.*}}, %[[ARG2]] overflow<nsw> : index
+// CHECK:             %[[ADD0:.*]] = arith.addi %[[ARG0]], %[[MUL]] overflow<nsw> : index
 // CHECK:             %[[ADD1:.*]] = arith.addi %[[ADD0]], %[[ARG2]] overflow<nsw> : index
 // CHECK:             scf.yield %[[ADD1]] : index
 // CHECK:           }
diff --git a/flang/test/Fir/FirToSCF/do-loop.fir b/flang/test/Fir/FirToSCF/do-loop.fir
index eba01c5893596..6d0813d10178a 100644
--- a/flang/test/Fir/FirToSCF/do-loop.fir
+++ b/flang/test/Fir/FirToSCF/do-loop.fir
@@ -13,8 +13,8 @@
 // CHECK:           %[[VAL_7:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_8:.*]] = arith.constant 1 : index
 // CHECK:           scf.for %[[VAL_9:.*]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] {
-// CHECK:             %[[VAL_10:.*]] = arith.muli %[[VAL_9]], %[[VAL_0]] : index
-// CHECK:             %[[VAL_11:.*]] = arith.addi %[[VAL_0]], %[[VAL_10]] : index
+// CHECK:             %[[VAL_10:.*]] = arith.muli %[[VAL_9]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:             %[[VAL_11:.*]] = arith.addi %[[VAL_0]], %[[VAL_10]] overflow<nsw> : index
 // CHECK:             %[[VAL_12:.*]] = fir.array_coor %[[ARG0]](%[[VAL_2]]) %[[VAL_11]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             fir.store %[[VAL_3]] to %[[VAL_12]] : !fir.ref<i32>
 // CHECK:           }
@@ -49,8 +49,8 @@ func.func @simple_loop(%arg0: !fir.ref<!fir.array<100xi32>>) {
 // CHECK: %[[C1:.*]] = arith.constant 1 : index
 // CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[TRIP]] step %[[C1]] {
 // CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
-// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[STEP]] : i32
-// CHECK:   %[[IV:.*]] = arith.addi %[[LB]], %[[SCALED]] : i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[STEP]] overflow<nsw> : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[LB]], %[[SCALED]] overflow<nsw> : i32
 // CHECK:   fir.store %[[IV]] to %[[ADDR]] : !fir.ref<i32>
 // CHECK: }
 // CHECK-NOT: iter_args
@@ -73,8 +73,8 @@ func.func @typed_loop(%lb: i32, %ub: i32, %step: i32,
 // CHECK: %[[C1_IDX:.*]] = arith.constant 1 : index
 // CHECK: %[[RES:.*]] = scf.for %[[I:.*]] = %[[C0_IDX]] to %[[TRIP]] step %[[C1_IDX]] iter_args(%[[ACC:.*]] = %[[C0]]) -> (i32) {
 // CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
-// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[C1]] : i32
-// CHECK:   %[[IV:.*]] = arith.addi %[[C1]], %[[SCALED]] : i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[C1]] overflow<nsw> : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[C1]], %[[SCALED]] overflow<nsw> : i32
 // CHECK:   %[[SUM:.*]] = arith.addi %[[ACC]], %[[IV]] : i32
 // CHECK:   scf.yield %[[SUM]] : i32
 // CHECK: }
@@ -109,8 +109,8 @@ func.func @typed_loop_iter_args(%addr: !fir.ref<i32>) -> i32 {
 // CHECK: %[[C1_IDX:.*]] = arith.constant 1 : index
 // CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[TRIP]] step %[[C1_IDX]] {
 // CHECK:   %[[I_TYPED:.*]] = fir.convert %[[I]] : (index) -> i32
-// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[CM1]] : i32
-// CHECK:   %[[IV:.*]] = arith.addi %[[C10]], %[[SCALED]] : i32
+// CHECK:   %[[SCALED:.*]] = arith.muli %[[I_TYPED]], %[[CM1]] overflow<nsw> : i32
+// CHECK:   %[[IV:.*]] = arith.addi %[[C10]], %[[SCALED]] overflow<nsw> : i32
 // CHECK:   fir.store %[[IV]] to %[[ADDR]] : !fir.ref<i32>
 // CHECK: }
 // CHECK-NOT: iter_args
@@ -139,8 +139,8 @@ func.func @typed_loop_negative_step(%addr: !fir.ref<i32>) {
 // CHECK:           %[[VAL_8:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_9:.*]] = arith.constant 1 : index
 // CHECK:           scf.for %[[VAL_10:.*]] = %[[VAL_8]] to %[[VAL_7]] step %[[VAL_9]] {
-// CHECK:             %[[VAL_11:.*]] = arith.muli %[[VAL_10]], %[[VAL_2]] : index
-// CHECK:             %[[VAL_12:.*]] = arith.addi %[[VAL_0]], %[[VAL_11]] : index
+// CHECK:             %[[VAL_11:.*]] = arith.muli %[[VAL_10]], %[[VAL_2]] overflow<nsw> : index
+// CHECK:             %[[VAL_12:.*]] = arith.addi %[[VAL_0]], %[[VAL_11]] overflow<nsw> : index
 // CHECK:             %[[VAL_13:.*]] = fir.array_coor %[[ARG0]](%[[VAL_3]]) %[[VAL_12]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             fir.store %[[VAL_4]] to %[[VAL_13]] : !fir.ref<i32>
 // CHECK:           }
@@ -174,8 +174,8 @@ func.func @loop_with_negtive_step(%arg0: !fir.ref<!fir.array<100xi32>>) {
 // CHECK:           %[[VAL_7:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_8:.*]] = arith.constant 1 : index
 // CHECK:           %[[VAL_9:.*]] = scf.for %[[VAL_10:.*]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] iter_args(%[[VAL_11:.*]] = %[[VAL_1]]) -> (i32) {
-// CHECK:             %[[VAL_12:.*]] = arith.muli %[[VAL_10]], %[[VAL_0]] : index
-// CHECK:             %[[VAL_13:.*]] = arith.addi %[[VAL_0]], %[[VAL_12]] : index
+// CHECK:             %[[VAL_12:.*]] = arith.muli %[[VAL_10]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:             %[[VAL_13:.*]] = arith.addi %[[VAL_0]], %[[VAL_12]] overflow<nsw> : index
 // CHECK:             %[[VAL_14:.*]] = fir.array_coor %[[ARG0]](%[[VAL_3]]) %[[VAL_13]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             %[[VAL_15:.*]] = fir.load %[[VAL_14]] : !fir.ref<i32>
 // CHECK:             %[[VAL_16:.*]] = arith.addi %[[VAL_11]], %[[VAL_15]] : i32
@@ -215,8 +215,8 @@ func.func @loop_with_results(%arg0: !fir.ref<!fir.array<100xi32>>, %arg1: !fir.r
 // CHECK:           %[[VAL_8:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_9:.*]] = arith.constant 1 : index
 // CHECK:           %[[VAL_10:.*]]:2 = scf.for %[[VAL_11:.*]] = %[[VAL_8]] to %[[VAL_7]] step %[[VAL_9]] iter_args(%[[VAL_12:.*]] = %[[VAL_0]], %[[VAL_13:.*]] = %[[VAL_1]]) -> (index, i32) {
-// CHECK:             %[[VAL_14:.*]] = arith.muli %[[VAL_11]], %[[VAL_0]] : index
-// CHECK:             %[[VAL_15:.*]] = arith.addi %[[VAL_0]], %[[VAL_14]] : index
+// CHECK:             %[[VAL_14:.*]] = arith.muli %[[VAL_11]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:             %[[VAL_15:.*]] = arith.addi %[[VAL_0]], %[[VAL_14]] overflow<nsw> : index
 // CHECK:             %[[VAL_16:.*]] = fir.array_coor %[[ARG0]](%[[VAL_4]]) %[[VAL_15]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             %[[VAL_17:.*]] = fir.load %[[VAL_16]] : !fir.ref<i32>
 // CHECK:             %[[VAL_18:.*]] = arith.addi %[[VAL_15]], %[[VAL_0]] overflow<nsw> : index
@@ -310,8 +310,8 @@ func.func @loop_with_final_value_and_result() {
 // CHECK:           %[[CONSTANT_4:.*]] = arith.constant 1 : index
 // PARALLEL:        scf.parallel (%[[VAL_0:.*]]) = (%[[CONSTANT_3]]) to (%[[DIVSI_0]]) step (%[[CONSTANT_4]]) {
 // NO-PARALLEL:     scf.for %[[VAL_0:.*]] = %[[CONSTANT_3]] to %[[DIVSI_0]] step %[[CONSTANT_4]] {
-// CHECK:             %[[MULI_0:.*]] = arith.muli %[[VAL_0]], %[[CONSTANT_0]] : index
-// CHECK:             %[[ADDI_1:.*]] = arith.addi %[[CONSTANT_0]], %[[MULI_0]] : index
+// CHECK:             %[[MULI_0:.*]] = arith.muli %[[VAL_0]], %[[CONSTANT_0]] overflow<nsw> : index
+// CHECK:             %[[ADDI_1:.*]] = arith.addi %[[CONSTANT_0]], %[[MULI_0]] overflow<nsw> : index
 // CHECK:             %[[ARRAY_COOR_0:.*]] = fir.array_coor %[[ARG0]](%[[SHAPE_0]]) %[[ADDI_1]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             fir.store %[[CONSTANT_2]] to %[[ARRAY_COOR_0]] : !fir.ref<i32>
 // PARALLEL:          scf.reduce
@@ -348,8 +348,8 @@ func.func @loop_with_unordered_attr(%arg0: !fir.ref<!fir.array<100xi32>>) {
 // CHECK:           %[[VAL_8:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_9:.*]] = arith.constant 1 : index
 // CHECK:           scf.for %[[VAL_10:.*]] = %[[VAL_8]] to %[[VAL_7]] step %[[VAL_9]] {
-// CHECK:             %[[VAL_11:.*]] = arith.muli %[[VAL_10]], %[[VAL_0]] : index
-// CHECK:             %[[VAL_12:.*]] = arith.addi %[[VAL_0]], %[[VAL_11]] : index
+// CHECK:             %[[VAL_11:.*]] = arith.muli %[[VAL_10]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:             %[[VAL_12:.*]] = arith.addi %[[VAL_0]], %[[VAL_11]] overflow<nsw> : index
 // CHECK:             %[[VAL_13:.*]] = fir.array_coor %[[ARG0]](%[[VAL_4]]) %[[VAL_12]] : (!fir.ref<!fir.array<100xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
 // CHECK:             %[[VAL_14:.*]] = fir.load %[[VAL_13]] : !fir.ref<i32>
 // CHECK:             %[[VAL_15:.*]] = fir.load %[[VAL_3]] : !fir.ref<i32>
@@ -392,16 +392,16 @@ func.func @loop_with_attribute(%arg0: !fir.ref<!fir.array<100xi32>>, %arg1: !fir
 // CHECK:           %[[VAL_7:.*]] = arith.constant 0 : index
 // CHECK:           %[[VAL_8:.*]] = arith.constant 1 : index
 // CHECK:           scf.for %[[VAL_9:.*]] = %[[VAL_7]] to %[[VAL_6]] step %[[VAL_8]] {
-// CHECK:             %[[VAL_10:.*]] = arith.muli %[[VAL_9]], %[[VAL_0]] : index
-// CHECK:             %[[VAL_11:.*]] = arith.addi %[[VAL_0]], %[[VAL_10]] : index
+// CHECK:             %[[VAL_10:.*]] = arith.muli %[[VAL_9]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:             %[[VAL_11:.*]] = arith.addi %[[VAL_0]], %[[VAL_10]] overflow<nsw> : index
 // CHECK:             %[[VAL_12:.*]] = arith.subi %[[VAL_2]], %[[VAL_0]] : index
 // CHECK:             %[[VAL_13:.*]] = arith.addi %[[VAL_12]], %[[VAL_0]] : index
 // CHECK:             %[[VAL_14:.*]] = arith.divsi %[[VAL_13]], %[[VAL_0]] : index
 // CHECK:             %[[VAL_15:.*]] = arith.constant 0 : index
 // CHECK:             %[[VAL_16:.*]] = arith.constant 1 : index
 // CHECK:             scf.for %[[VAL_17:.*]] = %[[VAL_15]] to %[[VAL_14]] step %[[VAL_16]] {
-// CHECK:               %[[VAL_18:.*]] = arith.muli %[[VAL_17]], %[[VAL_0]] : index
-// CHECK:               %[[VAL_19:.*]] = arith.addi %[[VAL_0]], %[[VAL_18]] : index
+// CHECK:               %[[VAL_18:.*]] = arith.muli %[[VAL_17]], %[[VAL_0]] overflow<nsw> : index
+// CHECK:               %[[VAL_19:.*]] = arith.addi %[[VAL_0]], %[[VAL_18]] overflow<nsw> : index
 // CHECK:               %[[VAL_20:.*]] = fir.array_coor %[[ARG0]](%[[VAL_3]]) %[[VAL_19]], %[[VAL_11]] : (!fir.ref<!fir.array<100x100xi32>>, !fir.shape<2>, index, index) -> !fir.ref<i32>
 // CHECK:               fir.store %[[VAL_1]] to %[[VAL_20]] : !fir.ref<i32>
 // CHECK:             }
diff --git a/flang/test/Fir/FirToSCF/normalize.fir b/flang/test/Fir/FirToSCF/normalize.fir
index 6e43f8664586a..e022d7e9dbe95 100644
--- a/flang/test/Fir/FirToSCF/normalize.fir
+++ b/flang/test/Fir/FirToSCF/normalize.fir
@@ -20,8 +20,8 @@
 // CHECK-NEXT:  [[ADD:%[0-9]+]] = arith.addi [[SUB]], [[STEP]] : index
 // CHECK-NEXT:  [[DIV:%[0-9]+]] = arith.divsi [[ADD]], [[STEP]] : index
 // CHECK:       scf.for %arg4 = %c0{{.*}} to [[DIV]] step %c1{{.*}}
-// CHECK-NEXT:    [[IVMUL:%[0-9]+]] = arith.muli %arg4, [[STEP]] : index
-// CHECK-NEXT:    [[IVADD:%[0-9]+]] = arith.addi [[LB]], [[IVMUL]] : index
+// CHECK-NEXT:    [[IVMUL:%[0-9]+]] = arith.muli %arg4, [[STEP]] overflow<nsw> : index
+// CHECK-NEXT:    [[IVADD:%[0-9]+]] = arith.addi [[LB]], [[IVMUL]] overflow<nsw> : index
 // CHECK-NEXT:    memref.store [[IVADD]], %arg3[]
 
 func.func @unknown_step_(%arg0: i64, %arg1: i64, %arg2: i64, %arg3: memref<index>) {
@@ -52,8 +52,8 @@ func.func @unknown_step_(%arg0: i64, %arg1: i64, %arg2: i64, %arg3: memref<index
 // CHECK-NEXT:  [[ADD:%[0-9]+]] = arith.addi [[SUB]], [[STEP]] : index
 // CHECK-NEXT:  [[DIV:%[0-9]+]] = arith.divsi [[ADD]], [[STEP]] : index
 // CHECK:       scf.for %arg1 = %c0{{.*}} to [[DIV]] step %c1{{.*}}
-// CHECK-NEXT:    [[IVMUL:%[0-9]+]] = arith.muli %arg1, [[STEP]] : index
-// CHECK-NEXT:    [[IVADD:%[0-9]+]] = arith.addi [[LB]], [[IVMUL]] : index
+// CHECK-NEXT:    [[IVMUL:%[0-9]+]] = arith.muli %arg1, [[STEP]] overflow<nsw> : index
+// CHECK-NEXT:    [[IVADD:%[0-9]+]] = arith.addi [[LB]], [[IVMUL]] overflow<nsw> : index
 // CHECK-NEXT:    memref.store [[IVADD]], %arg0[]
 
 func.func @negative_step_(%arg0: memref<index>) {



More information about the flang-commits mailing list