[Mlir-commits] [mlir] [mlir][SCFToControlFlow] Mark the scf.for increment as non-wrapping (PR #230891)

Slava Zakharin llvmlistbot at llvm.org
Sat Oct 10 22:07:54 PDT 2026


https://github.com/vzakhari created https://github.com/llvm/llvm-project/pull/230891

`scf.for` makes it undefined behavior for `LB + n*Step` not to be representable in the induction variable type (#203713), so no increment of the induction variable can wrap under the interpretation of the bounds. Emit the increment with `overflow<nsw>`, or `overflow<nuw>` for loops with `unsignedCmp`.

Without the flag, LLVM cannot prove that an induction variable that starts non-negative and has a non-negative step stays non-negative.

Assisted-by: Cursor

>From 8484ab6457ae5b235c7da19ccf6d943cf9b70689 Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Fri, 9 Oct 2026 19:53:34 -0700
Subject: [PATCH] [mlir][SCFToControlFlow] Mark the scf.for increment as
 non-wrapping

`scf.for` makes it undefined behavior for `LB + n*Step` not to be
representable in the induction variable type (#203713), so no increment
of the induction variable can wrap under the interpretation of the
bounds. Emit the increment with `overflow<nsw>`, or `overflow<nuw>` for
loops with `unsignedCmp`.

Without the flag, LLVM cannot prove that an induction variable that
starts non-negative and has a non-negative step stays non-negative.

Assisted-by: Cursor
---
 .../SCFToControlFlow/SCFToControlFlow.cpp     |  9 ++++++-
 .../SCFToControlFlow/convert-to-cfg.mlir      | 24 ++++++++++---------
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/mlir/lib/Conversion/SCFToControlFlow/SCFToControlFlow.cpp b/mlir/lib/Conversion/SCFToControlFlow/SCFToControlFlow.cpp
index 0374138e8cbe3..4d3cb36f0367c 100644
--- a/mlir/lib/Conversion/SCFToControlFlow/SCFToControlFlow.cpp
+++ b/mlir/lib/Conversion/SCFToControlFlow/SCFToControlFlow.cpp
@@ -358,7 +358,14 @@ LogicalResult ForLowering::matchAndRewrite(ForOp forOp,
   Operation *terminator = lastBodyBlock->getTerminator();
   rewriter.setInsertionPointToEnd(lastBodyBlock);
   auto step = forOp.getStep();
-  auto stepped = arith::AddIOp::create(rewriter, loc, iv, step).getResult();
+  // `scf.for` requires `LB + n*Step` to be representable in the induction
+  // variable type, so the increment cannot wrap in the signed (or, with
+  // `unsignedCmp`, unsigned) interpretation of the bounds.
+  arith::IntegerOverflowFlags overflowFlags =
+      forOp.getUnsignedCmp() ? arith::IntegerOverflowFlags::nuw
+                             : arith::IntegerOverflowFlags::nsw;
+  auto stepped =
+      arith::AddIOp::create(rewriter, loc, iv, step, overflowFlags).getResult();
   if (!stepped)
     return failure();
 
diff --git a/mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir b/mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir
index 47e085509dd91..8fcca79914373 100644
--- a/mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir
+++ b/mlir/test/Conversion/SCFToControlFlow/convert-to-cfg.mlir
@@ -8,7 +8,7 @@
 //  CHECK-NEXT:    cf.cond_br %{{.*}}, ^bb2, ^bb3
 //  CHECK-NEXT:  ^bb2:   // pred: ^bb1
 //  CHECK-NEXT:    %{{.*}} = arith.constant 1 : index
-//  CHECK-NEXT:    %[[iv:.*]] = arith.addi %{{.*}}, %{{.*}} : index
+//  CHECK-NEXT:    %[[iv:.*]] = arith.addi %{{.*}}, %{{.*}} overflow<nsw> : index
 //  CHECK-NEXT:    cf.br ^bb1(%[[iv]] : index)
 //  CHECK-NEXT:  ^bb3:   // pred: ^bb1
 //  CHECK-NEXT:    return
@@ -26,7 +26,7 @@ func.func @simple_std_for_loop(%arg0 : index, %arg1 : index, %arg2 : index) {
 //  CHECK-NEXT:    cf.cond_br %{{.*}}, ^bb2, ^bb3
 //  CHECK-NEXT:  ^bb2:   // pred: ^bb1
 //  CHECK-NEXT:    %{{.*}} = arith.constant 1 : index
-//  CHECK-NEXT:    %[[iv:.*]] = arith.addi %{{.*}}, %{{.*}} : index
+//  CHECK-NEXT:    %[[iv:.*]] = arith.addi %{{.*}}, %{{.*}} overflow<nuw> : index
 //  CHECK-NEXT:    cf.br ^bb1(%[[iv]] : index)
 //  CHECK-NEXT:  ^bb3:   // pred: ^bb1
 //  CHECK-NEXT:    return
@@ -50,10 +50,10 @@ func.func @unsigned_loop(%arg0 : index, %arg1 : index, %arg2 : index) {
 //  CHECK-NEXT:    cf.cond_br %[[cond1]], ^bb4, ^bb5
 //  CHECK-NEXT:  ^bb4:   // pred: ^bb3
 //  CHECK-NEXT:    %{{.*}} = arith.constant 1 : index
-//  CHECK-NEXT:    %[[iv1:.*]] = arith.addi %{{.*}}, %{{.*}} : index
+//  CHECK-NEXT:    %[[iv1:.*]] = arith.addi %{{.*}}, %{{.*}} overflow<nsw> : index
 //  CHECK-NEXT:    cf.br ^bb3(%[[iv1]] : index)
 //  CHECK-NEXT:  ^bb5:   // pred: ^bb3
-//  CHECK-NEXT:    %[[iv0:.*]] = arith.addi %{{.*}}, %{{.*}} : index
+//  CHECK-NEXT:    %[[iv0:.*]] = arith.addi %{{.*}}, %{{.*}} overflow<nsw> : index
 //  CHECK-NEXT:    cf.br ^bb1(%[[iv0]] : index)
 //  CHECK-NEXT:  ^bb6:   // pred: ^bb1
 //  CHECK-NEXT:    return
@@ -147,7 +147,7 @@ func.func @simple_std_2_ifs(%arg0: i1) {
 //  CHECK-NEXT:   ^bb6:   // 2 preds: ^bb4, ^bb5
 //  CHECK-NEXT:     cf.br ^bb7
 //  CHECK-NEXT:   ^bb7:   // 2 preds: ^bb2, ^bb6
-//  CHECK-NEXT:     %[[iv0:.*]] = arith.addi %{{.*}}, %{{.*}} : index
+//  CHECK-NEXT:     %[[iv0:.*]] = arith.addi %{{.*}}, %{{.*}} overflow<nsw> : index
 //  CHECK-NEXT:     cf.br ^bb1(%[[iv0]] : index)
 //  CHECK-NEXT:   ^bb8:   // pred: ^bb1
 //  CHECK-NEXT:     return
@@ -258,10 +258,10 @@ func.func @nested_if_yield(%arg0: i1) -> (index) {
 // CHECK:           cf.cond_br [[VAL_9]], ^bb4, ^bb5
 // CHECK:         ^bb4:
 // CHECK:           [[VAL_10:%.*]] = arith.constant 1 : index
-// CHECK:           [[VAL_11:%.*]] = arith.addi [[VAL_8]], [[VAL_5]] : index
+// CHECK:           [[VAL_11:%.*]] = arith.addi [[VAL_8]], [[VAL_5]] overflow<nsw> : index
 // CHECK:           cf.br ^bb3([[VAL_11]] : index)
 // CHECK:         ^bb5:
-// CHECK:           [[VAL_12:%.*]] = arith.addi [[VAL_6]], [[VAL_4]] : index
+// CHECK:           [[VAL_12:%.*]] = arith.addi [[VAL_6]], [[VAL_4]] overflow<nsw> : index
 // CHECK:           cf.br ^bb1([[VAL_12]] : index)
 // CHECK:         ^bb6:
 // CHECK:           return
@@ -283,6 +283,8 @@ func.func @parallel_loop(%arg0 : index, %arg1 : index, %arg2 : index,
 // CHECK:           cf.cond_br
 // CHECK:           arith.cmpi ult
 // CHECK:           cf.cond_br
+// CHECK:           arith.addi {{.*}} overflow<nuw> : index
+// CHECK:           arith.addi {{.*}} overflow<nuw> : index
 func.func @unsigned_parallel_loop(%arg0 : index, %arg1 : index, %arg2 : index,
                                   %arg3 : index, %arg4 : index) {
   %step = arith.constant 1 : index
@@ -306,7 +308,7 @@ func.func @unsigned_parallel_loop(%arg0 : index, %arg1 : index, %arg2 : index,
 //
 // CHECK:      ^[[BODY]]:
 // CHECK:        %[[SUM:.*]] = arith.addf %[[ITER_ARG0]], %[[ITER_ARG1]] : f32
-// CHECK:        %[[STEPPED:.*]] = arith.addi %[[ITER]], %[[STEP]] : index
+// CHECK:        %[[STEPPED:.*]] = arith.addi %[[ITER]], %[[STEP]] overflow<nsw> : index
 // CHECK:        cf.br ^[[COND]](%[[STEPPED]], %[[SUM]], %[[SUM]] : index, f32, f32)
 //
 // CHECK:      ^[[CONTINUE]]:
@@ -375,7 +377,7 @@ func.func @simple_parallel_reduce_loop(%arg0: index, %arg1: index,
   // CHECK: ^[[BODY]]:
   // CHECK:   %[[CST:.*]] = arith.constant 4.2
   // CHECK:   %[[PROD:.*]] = arith.mulf %[[ITER_ARG]], %[[CST]]
-  // CHECK:   %[[INCR:.*]] = arith.addi %[[ITER]], %[[STEP]]
+  // CHECK:   %[[INCR:.*]] = arith.addi %[[ITER]], %[[STEP]] overflow<nsw>
   // CHECK:   cf.br ^[[COND]](%[[INCR]], %[[PROD]]
 
   // The continuation block has access to the (last value of) reduction.
@@ -602,7 +604,7 @@ func.func @ifs_in_parallel(%arg1: index, %arg2: index, %arg3: index, %arg4: i1,
   // CHECK: ^[[IF2_CONT]]:
   // CHECK:   cf.br ^[[IF1_CONT]]
   // CHECK: ^[[IF1_CONT]]:
-  // CHECK:   %{{.*}} = arith.addi %[[LOOP_IV]], %[[ARG2]] : index
+  // CHECK:   %{{.*}} = arith.addi %[[LOOP_IV]], %[[ARG2]] overflow<nsw> : index
   // CHECK:   cf.br ^[[LOOP_LATCH]](%{{.*}} : index)
   scf.parallel (%i) = (%arg1) to (%arg2) step (%arg3) {
     scf.if %arg4 {
@@ -724,7 +726,7 @@ func.func @index_switch_large_cases(%i: index) {
 //       CHECK:   cf.cond_br %[[cmpi]], ^[[bb2:.*]], ^[[bb3:.*]]
 //       CHECK: ^[[bb2]]:
 //       CHECK:   "test.foo"(%[[arg0]])
-//       CHECK:   %[[addi:.*]] = arith.addi %[[arg0]], %[[c1]]
+//       CHECK:   %[[addi:.*]] = arith.addi %[[arg0]], %[[c1]] overflow<nsw>
 //       CHECK:   cf.br ^[[bb1]](%[[addi]] : index)
 //       CHECK: ^[[bb3]]:
 //       CHECK:   return



More information about the Mlir-commits mailing list