[flang-commits] [flang] [flang] Try to keep overflow flags in `FIRToMemRef::canonicalizeIndex'. (PR #220127)

via flang-commits flang-commits at lists.llvm.org
Mon Aug 31 16:48:57 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Valery Dmitriev (valerydmit)

<details>
<summary>Changes</summary>


The method specifically runs on every `fir.array_coor` subscript. For `arith.addi` it recursively canonicalizes both operands and then unconditionally rebuilds the op defaulting flags to `none`. Thus subscript operand becomes a flagless clone, with the original one left behind dead. The patch makes a couple of changes: (1) use the original op if unmodified and (2) when its type preserved keep overflow flags of the original.

Assisted-by: Claude Opus 5

---
Full diff: https://github.com/llvm/llvm-project/pull/220127.diff


3 Files Affected:

- (modified) flang/lib/Optimizer/Transforms/FIRToMemRef.cpp (+16-5) 
- (added) flang/test/Transforms/FIRToMemRef/index-overflow-flags.mlir (+54) 
- (modified) flang/test/Transforms/FIRToMemRef/slice.mlir (+1-2) 


``````````diff
diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 5851022b1bc47..3975cddeed75b 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -1333,10 +1333,12 @@ FIRToMemRef::getFIRConvert(Operation *memOp, Operation *op,
 ///      `index_cast` if they need an index-typed result.
 ///
 ///   4. `arith.addi %a, %b`
-///      -> recursively canonicalize both operands, and if their result
-///      types match, build a new `arith.addi` at the same location. If
-///      the canonicalized operand types diverge, returns the original op
-///      untouched (the caller can still `index_cast` externally).
+///      -> recursively canonicalize both operands. If neither operand
+///      changed, the original op is returned as-is. Otherwise, if the
+///      canonicalized operand types match, build a new `arith.addi` at the
+///      same location, carrying over the original overflow flags.
+///      If the canonicalized operand types diverge, returns the original
+///      op untouched (the caller can still `index_cast` externally).
 ///
 /// Only these four patterns fire -- this is intentionally a narrow peephole,
 /// not a general folder. Multiplication, sub, cast chains through other ops,
@@ -1372,8 +1374,17 @@ Value FIRToMemRef::canonicalizeIndex(Value index,
   if (auto add = dyn_cast<arith::AddIOp>(op)) {
     Value lhs = canonicalizeIndex(add.getLhs(), rewriter);
     Value rhs = canonicalizeIndex(add.getRhs(), rewriter);
-    if (lhs.getType() == rhs.getType())
+    // Neither operand simplified, so a rebuilt op would be an exact duplicate.
+    // Reuse the original instead.
+    if (lhs == add.getLhs() && rhs == add.getRhs())
+      return index;
+    if (lhs.getType() == rhs.getType()) {
+      // Carry over overflow flags when width unchanged only.
+      if (lhs.getType() == add.getType())
+        return arith::AddIOp::create(rewriter, op->getLoc(), lhs, rhs,
+                                     add.getOverflowFlags());
       return arith::AddIOp::create(rewriter, op->getLoc(), lhs, rhs);
+    }
   }
   return index;
 }
diff --git a/flang/test/Transforms/FIRToMemRef/index-overflow-flags.mlir b/flang/test/Transforms/FIRToMemRef/index-overflow-flags.mlir
new file mode 100644
index 0000000000000..7e1befd8121a8
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/index-overflow-flags.mlir
@@ -0,0 +1,54 @@
+// Test that overflow flags on subscript arithmetic survive the index
+// canonicalization performed while lowering fir.array_coor.
+
+// RUN: fir-opt %s --fir-to-memref --allow-unregistered-dialect | FileCheck %s
+
+// An `arith.addi` whose operands need no canonicalization must be reused
+// as-is, keeping `overflow<nsw>`.
+//
+// CHECK-LABEL: func.func @addi_nsw_preserved
+// CHECK:       [[I:%.+]]    = memref.load
+// CHECK:       [[J:%.+]]    = memref.load
+// CHECK:       [[SUM:%.+]]  = arith.addi [[I]], [[J]] overflow<nsw> : i32
+// CHECK-NOT:   arith.addi {{.*}} : i32
+// CHECK:       [[CAST:%.+]] = arith.index_cast [[SUM]] : i32 to index
+func.func @addi_nsw_preserved(%arg0: !fir.ref<!fir.array<100xf32>>, %arg1: !fir.ref<i32>, %arg2: !fir.ref<i32>) {
+  %c100 = arith.constant 100 : index
+  %dscope = fir.undefined !fir.dscope
+  %shape = fir.shape %c100 : (index) -> !fir.shape<1>
+  %a = fir.declare %arg0(%shape) dummy_scope %dscope {uniq_name = "a"} : (!fir.ref<!fir.array<100xf32>>, !fir.shape<1>, !fir.dscope) -> !fir.ref<!fir.array<100xf32>>
+  %i = fir.declare %arg1 dummy_scope %dscope {uniq_name = "i"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %j = fir.declare %arg2 dummy_scope %dscope {uniq_name = "j"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %iv = fir.load %i : !fir.ref<i32>
+  %jv = fir.load %j : !fir.ref<i32>
+  %sum = arith.addi %iv, %jv overflow<nsw> : i32
+  %addr = fir.array_coor %a(%shape) %sum : (!fir.ref<!fir.array<100xf32>>, !fir.shape<1>, i32) -> !fir.ref<f32>
+  %v = fir.load %addr : !fir.ref<f32>
+  return
+}
+
+// When canonicalization peels the `arith.extsi` off both operands the add is
+// rebuilt at the narrower width, so `overflow<nsw>` must not carry over: a
+// 64-bit add that cannot wrap can still wrap in 32 bits.
+//
+// CHECK-LABEL: func.func @addi_nsw_dropped_on_narrowing
+// CHECK:       [[I:%.+]]    = memref.load
+// CHECK:       [[J:%.+]]    = memref.load
+// CHECK:       [[SUM:%.+]]  = arith.addi [[I]], [[J]] : i32
+// CHECK:       [[CAST:%.+]] = arith.index_cast [[SUM]] : i32 to index
+func.func @addi_nsw_dropped_on_narrowing(%arg0: !fir.ref<!fir.array<100xf32>>, %arg1: !fir.ref<i32>, %arg2: !fir.ref<i32>) {
+  %c100 = arith.constant 100 : index
+  %dscope = fir.undefined !fir.dscope
+  %shape = fir.shape %c100 : (index) -> !fir.shape<1>
+  %a = fir.declare %arg0(%shape) dummy_scope %dscope {uniq_name = "a"} : (!fir.ref<!fir.array<100xf32>>, !fir.shape<1>, !fir.dscope) -> !fir.ref<!fir.array<100xf32>>
+  %i = fir.declare %arg1 dummy_scope %dscope {uniq_name = "i"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %j = fir.declare %arg2 dummy_scope %dscope {uniq_name = "j"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %iv = fir.load %i : !fir.ref<i32>
+  %jv = fir.load %j : !fir.ref<i32>
+  %ie = arith.extsi %iv : i32 to i64
+  %je = arith.extsi %jv : i32 to i64
+  %sum = arith.addi %ie, %je overflow<nsw> : i64
+  %addr = fir.array_coor %a(%shape) %sum : (!fir.ref<!fir.array<100xf32>>, !fir.shape<1>, i64) -> !fir.ref<f32>
+  %v = fir.load %addr : !fir.ref<f32>
+  return
+}
diff --git a/flang/test/Transforms/FIRToMemRef/slice.mlir b/flang/test/Transforms/FIRToMemRef/slice.mlir
index 737babd4733a0..a1e0fba75aaf4 100644
--- a/flang/test/Transforms/FIRToMemRef/slice.mlir
+++ b/flang/test/Transforms/FIRToMemRef/slice.mlir
@@ -297,8 +297,7 @@ func.func @extract_column(%arg0: !fir.ref<!fir.array<100x5xf32>> {fir.bindc_name
 // CHECK:         [[BOXADDR2:%.*]] = fir.box_addr [[EMBOX]] : (!fir.box<!fir.array<7xf32>>) -> !fir.ref<!fir.array<7xf32>>
 // CHECK:         [[CONVERT:%.*]] = fir.convert [[BOXADDR2]] : (!fir.ref<!fir.array<7xf32>>) -> memref<7xf32>
 // CHECK:         %[[C1_0:.*]] = arith.constant 1 : index
-// CHECK:         [[ADD2:%.*]] = arith.addi %[[ARG0]], %[[C_NEG1]] : index
-// CHECK:         [[SUB1:%.*]] = arith.subi [[ADD2]], %[[C0]] : index
+// CHECK:         [[SUB1:%.*]] = arith.subi [[ADD1]], %[[C0]] : index
 // CHECK:         [[MUL1:%.*]] = arith.muli [[SUB1]], %[[C1_0]] : index
 // CHECK:         [[SUB2:%[0-9]+]] = arith.subi %[[C0]], %[[C0]] : index
 // CHECK:         [[ADD3:%.*]] = arith.addi [[MUL1]], [[SUB2]] : index

``````````

</details>


https://github.com/llvm/llvm-project/pull/220127


More information about the flang-commits mailing list