[flang-commits] [flang] [FIRToMemRef] Fix wrong indexing for converted array_coor over sliced fir.embox (PR #207749)
Kareem Ergawy via flang-commits
flang-commits at lists.llvm.org
Tue Jul 7 03:57:12 PDT 2026
https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/207749
>From 068646c2a1771803d6ba051e06b7d4ec84b8b94c Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Sun, 5 Jul 2026 06:19:56 -0700
Subject: [PATCH 1/2] [FIRToMemRef] Fix wrong indexing for converted array_coor
over sliced fir.embox
FIRToMemRef::convertArrayCoorOp routes through getMemrefIndices, which
only folds the first `rank` triples of sliceInfo.sliceVec into the memref
indices (i.e. the array_coor's own slice); the embox's slice triples --
which sit at [rank*3 .. 2*rank*3-1] when both are present -- were dropped.
Three consequences, three fixes here:
1. Non-collapsed embox slice lbs contribute (lb - 1) per Fortran dim to
each memref index. Fold them in in memref order (reversed Fortran
order) so the reinterpret_cast view lands at the right column.
2. The shapeVec-else stride path used shapeVec[0..rank-1] to build the
outer strides. With both slices present, that's the box's (slice's)
extents. Use shapeVec[rank..2*rank-1] instead -- the parent's extents
-- so the outer stride is the parent's leading dim rather than the
slice's own size.
3. Rank-reducing embox slices (undef ub/step triples) have no memref
index position for their (lb - 1) shift. Fold the collapsed dim's
(lb - 1) * parent_stride into the reinterpret_cast's flat offset and
override the corresponding memref index entry to 0.
Co-Authored-By: Claude Opus 4.7 <noreply at anthropic.com>
---
.../lib/Optimizer/Transforms/FIRToMemRef.cpp | 150 +++++++++++++++++-
.../FIRToMemRef/emboxed-slice-array-coor.mlir | 65 ++++++++
.../emboxed-slice-rank-reduction.mlir | 82 ++++++++++
.../FIRToMemRef/emboxed-slice-stride.mlir | 67 ++++++++
4 files changed, 362 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
create mode 100644 flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
create mode 100644 flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 1dcc056387c48..d86d573ef63af 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -169,6 +169,10 @@ class FIRToMemRef : public fir::impl::FIRToMemRefBase<FIRToMemRef> {
template <typename OpTy>
void collectSliceInfoFrom(OpTy op, SliceInfo &info) const;
+ void foldSliceLbIntoIndices(SmallVectorImpl<Value> &indices,
+ fir::EmboxOp embox, PatternRewriter &rewriter,
+ Location loc) const;
+
void populateShapeAndShift(SmallVectorImpl<Value> &shapeVec,
SmallVectorImpl<Value> &shiftVec,
fir::ShapeShiftOp shift) const;
@@ -456,6 +460,100 @@ static Value castTypeToIndexType(Value originalValue,
originalValue);
}
+/// Fold the embox's slice per-dim (lb - 1) shift into `indices` after
+/// `getMemrefIndices` has produced them. Handles both range and rank-reducing
+/// (scalar-subscript) slice triples uniformly.
+///
+/// Necessary because `getMemrefIndices` only consumes the first `rank*3`
+/// entries of `sliceInfo.sliceVec` (the array_coor's own slice); the embox's
+/// triples sit at `sliceVec[rank*3 .. 2*rank*3-1]` and are otherwise dropped,
+/// leaving the memref access short by `(lb - 1)` per dim -- and, for
+/// collapsed dims, additionally polluted with the array_coor's own scalar
+/// subscript (typically a loop variable) that would drag the load off the
+/// target dim across iterations.
+///
+/// Behaviour, per Fortran dim `d` of the embox slice (memref position
+/// `m = parentRank - 1 - d`):
+/// - Range triple `(lb, ub, step)`:
+/// indices[m] += (lb - 1)
+/// Adds the (lb - 1) shift on top of whatever `getMemrefIndices` produced
+/// for that dim.
+/// - Scalar-subscript triple `(lb, undef, undef)`:
+/// indices[m] = (lb - 1)
+/// Overwrites the memref position outright. The dim is collapsed in the
+/// box's rank, but the reinterpret_cast still keeps a memref index slot
+/// for it (with the correct parent stride). Placing `(lb - 1)` there is
+/// mathematically equivalent to shifting the flat offset by
+/// `(lb - 1) * parentStride[d]`, without needing any parent-shape
+/// lookups from the caller.
+///
+/// Example A -- non-rank-reducing (2-D):
+///
+/// `fir.array_coor %box(...)[%innerSlice] %i, %j` with
+/// - array_coor slice: identity in both dims
+/// - embox slice : `fir.slice %c2,%c2,%c1, %c1,%c1,%c1`
+/// (Fortran dim 0 lb = 2, dim 1 lb = 1)
+///
+/// Incoming `indices` (memref order): [ %j', %i' ]
+/// Fold:
+/// - dim 0 (range): delta = 1 -> indices[1] += 1
+/// - dim 1 (range): delta = 0 -> indices[0] += 0
+/// Result: [ %j', %i' + 1 ]
+///
+/// Example B -- rank-reducing (3-D parent, dim 1 collapsed):
+///
+/// %parent : !fir.ref<!fir.array<3x2x2xi32>>
+/// %shape = fir.shape %c3, %c2, %c2
+/// %eslc = fir.slice %c1, %c3, %c1, %c2, %undef, %undef, %c1, %c2, %c1
+/// %box = fir.embox %parent(%shape) [%eslc]
+/// : !fir.box<!fir.array<3x2xi32>>
+/// %bshape = fir.shape %c3, %c2 ; box's rank-2 shape
+/// %addr = fir.array_coor %box(%bshape) %ii, %jj
+/// : (!fir.box<!fir.array<3x2xi32>>, !fir.shape<2>, index, index)
+/// -> !fir.ref<i32>
+///
+/// parentRank = 3. `getRankFromEmbox` returns the parent's rank, so
+/// `getMemrefIndices` builds a rank-3 index vector (memref order):
+/// indices = [ %jj', <ac scalar sub for collapsed dim>, %ii' ]
+/// ^ dim 2 ^ dim 1 (collapsed) ^ dim 0
+///
+/// Fold:
+/// - dim 0 (range, lb=1): indices[2] += 0
+/// - dim 1 (scalar, lb=2): indices[1] = 1 (overwrite)
+/// - dim 2 (range, lb=1): indices[0] += 0
+///
+/// Final: `memref.load %reinterpret_cast[%jj', 1, %ii'] : memref<?x?x?xi32,
+/// strided<[?, ?, ?], offset: ?>>` -- stride 3 on the middle position
+/// lands on the correct plane, `offset` stays at 0.
+void FIRToMemRef::foldSliceLbIntoIndices(SmallVectorImpl<Value> &indices,
+ fir::EmboxOp embox,
+ PatternRewriter &rewriter,
+ Location loc) const {
+ auto sliceOp = getSliceOp(embox.getSlice());
+ if (!sliceOp)
+ return;
+ auto triples = sliceOp.getTriples();
+ unsigned parentRank = triples.size() / 3;
+ if (parentRank == 0)
+ return;
+
+ auto isUndef = [](Value v) {
+ return v && v.getDefiningOp<fir::UndefOp>() != nullptr;
+ };
+ Value cOne = arith::ConstantIndexOp::create(rewriter, loc, 1);
+ for (unsigned d = 0; d < parentRank; ++d) {
+ unsigned m = parentRank - 1 - d;
+ if (m >= indices.size())
+ continue;
+ Value lb = castTypeToIndexType(triples[d * 3], rewriter);
+ Value delta = arith::SubIOp::create(rewriter, loc, lb, cOne);
+ bool isScalar = isUndef(triples[d * 3 + 1]) || isUndef(triples[d * 3 + 2]);
+ indices[m] = isScalar
+ ? delta
+ : arith::AddIOp::create(rewriter, loc, indices[m], delta);
+ }
+}
+
static bool shouldUseBoundaryBitcast(mlir::Type fromTy, mlir::Type toTy) {
auto isBitcastCompatibleScalarType = [](mlir::Type ty) {
return mlir::isa<mlir::IntegerType, mlir::FloatType, fir::LogicalType>(
@@ -814,13 +912,52 @@ FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp,
} else {
Value oneIdx =
arith::ConstantIndexOp::create(rewriter, arrayCoorOp->getLoc(), 1);
+ // shapeVec is populated by collectSliceInfoFrom in the order:
+ // [<arrayCoor's shape>, <embox/rebox's shape>]
+ // When both contribute (firMemrefIsEmbox && arrayCoorOp has a slice), the
+ // first `rank` entries are the *box's* shape (= slice extents) while the
+ // next `rank` are the underlying *parent's* extents. Strides for the
+ // reinterpret_cast must come from the parent's contiguous element strides,
+ // not the slice extents (using the latter yields an outer stride that
+ // walks by the slice's size instead of the parent's leading dim).
+ //
+ // Example -- 4x2 parent, slice a(1:2, :) keeping 2 elements per dim:
+ // %parent : !fir.ref<!fir.array<4x2xi32>>
+ // %shape = fir.shape %c4, %c2
+ // %eslc = fir.slice %c1, %c2, %c1, %c1, %c2, %c1 ; a(1:2, :)
+ // %box = fir.embox %parent(%shape) [%eslc]
+ // :
+ // !fir.box<!fir.array<2x2xi32>>
+ // ... %addr = fir.array_coor %box(...) [%innerSlc] %i, %j
+ //
+ // After collectSliceInfoFrom is called on both the array_coor and the
+ // embox, shapeVec becomes (four entries, Fortran order):
+ // [ box_dim0_extent(=2), box_dim1_extent(=2), ; array_coor shape
+ // parent_dim0_extent(=4), parent_dim1_extent(=2) ] ; embox shape
+ //
+ // For rank = 2 the loop below emits (memref order, outer -> inner):
+ // size[outer] = shapeVec[1] = 2 (slice's dim1)
+ // stride[outer] = shapeVec[parentShapeBase + 0] = 4 (parent's dim0)
+ // size[inner] = shapeVec[0] = 2 (slice's dim0)
+ // stride[inner] = 1
+ //
+ // Without the `parentShapeBase` shift, `stride[outer]` would be
+ // `shapeVec[0] = 2` -- the slice's own extent -- and successive outer
+ // steps would walk by 2 elements instead of the parent's 4-element row,
+ // clobbering the wrong columns.
+ const bool hasParentShape = firMemrefIsEmbox && arrayCoorOp.getSlice() &&
+ shapeVec.size() >= 2 * rank;
+ const unsigned parentShapeBase = hasParentShape ? rank : 0;
for (unsigned i = rank - 1; i > 0; --i) {
+ // Sizes are always the box/slice's visible extents (shapeVec[0..rank-1]).
Value size = shapeVec[i];
sizes.push_back(castTypeToIndexType(size, rewriter));
- Value stride = shapeVec[0];
+ // Strides use the parent's extents (via `parentShapeBase`).
+ Value stride = shapeVec[parentShapeBase + 0];
for (unsigned j = 1; j <= i - 1; ++j)
- stride = arith::MulIOp::create(rewriter, loc, shapeVec[j], stride);
+ stride = arith::MulIOp::create(rewriter, loc,
+ shapeVec[parentShapeBase + j], stride);
if (complexPartIdx)
stride = arith::MulIOp::create(
rewriter, loc, stride,
@@ -860,6 +997,15 @@ FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp,
Value offset = arith::ConstantIndexOp::create(rewriter, loc, 0);
+ // Fold the embox's slice contribution into the memref indices.
+ // getMemrefIndices only consumed the array_coor's own slice; the embox's
+ // triples are still unconsumed at this point. Handles both range and
+ // rank-reducing (scalar-subscript) dims uniformly.
+ if (firMemrefIsEmbox && !complexPartIdx && arrayCoorOp.getSlice()) {
+ auto embox = firMemref.getDefiningOp<fir::EmboxOp>();
+ foldSliceLbIntoIndices(indices, embox, rewriter, loc);
+ }
+
auto reinterpret = memref::ReinterpretCastOp::create(
rewriter, loc, memRefTy, convertedVal, offset, sizes, strides);
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
new file mode 100644
index 0000000000000..236b8f6a97dea
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
@@ -0,0 +1,65 @@
+// RUN: fir-opt %s --fir-to-memref | FileCheck %s
+
+// Verify that when both the embox and the array_coor carry a slice, the
+// embox's per-dim (lb - 1) shift is folded into the memref indices.
+
+func.func @emboxed_slice_array_coor(%arg0: !fir.ref<!fir.array<4x2xi32>>) -> i32 {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c4 = arith.constant 4 : index
+ %ci = arith.constant 1 : i64
+ %shape = fir.shape %c4, %c2 : (index, index) -> !fir.shape<2>
+ // Embox slice: a(2:2, 1:1) -- Fortran dim-0 lb = 2, dim-1 lb = 1.
+ %eslice = fir.slice %c2, %c2, %c1, %c1, %c1, %c1
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %box = fir.embox %arg0(%shape) [%eslice]
+ : (!fir.ref<!fir.array<4x2xi32>>, !fir.shape<2>, !fir.slice<2>)
+ -> !fir.box<!fir.array<1x1xi32>>
+ // Inner array_coor slice (identity 1:1:1 in both dims).
+ %ashape = fir.shape %c1, %c1 : (index, index) -> !fir.shape<2>
+ %islice = fir.slice %c1, %c1, %c1, %c1, %c1, %c1
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %addr = fir.array_coor %box(%ashape) [%islice] %ci, %c1
+ : (!fir.box<!fir.array<1x1xi32>>, !fir.shape<2>, !fir.slice<2>, i64, index)
+ -> !fir.ref<i32>
+ %v = fir.load %addr : !fir.ref<i32>
+ return %v : i32
+}
+
+// CHECK-LABEL: func.func @emboxed_slice_array_coor(
+// CHECK-SAME: %[[ARG0:.+]]: !fir.ref<!fir.array<4x2xi32>>) -> i32
+// CHECK-DAG: %[[C1:.+]] = arith.constant 1 : index
+// CHECK-DAG: %[[C2:.+]] = arith.constant 2 : index
+// CHECK-DAG: %[[C4:.+]] = arith.constant 4 : index
+
+// Parent fir.shape and embox fir.slice survive to the emitted module.
+// CHECK: %[[SHAPE:.+]] = fir.shape %[[C4]], %[[C2]] : (index, index) -> !fir.shape<2>
+// CHECK: %[[ESLICE:.+]] = fir.slice %[[C2]], %[[C2]], %[[C1]], %[[C1]], %[[C1]], %[[C1]] : ({{.+}}) -> !fir.slice<2>
+// CHECK: fir.embox %[[ARG0]](%[[SHAPE]]) [%[[ESLICE]]] : ({{.+}}) -> !fir.box<!fir.array<1x1xi32>>
+
+// Row-major memref view of the parent (col-major !fir.array<4x2>).
+// CHECK: %[[MEMREF:.+]] = fir.convert %[[ARG0]] : (!fir.ref<!fir.array<4x2xi32>>) -> memref<2x4xi32>
+
+// Anchor: `arith.constant 0 : index` appears exactly twice between the
+// fir.convert and the fold -- once for getMemrefIndices' zero placeholder,
+// then again as the reinterpret_cast offset. The fold's `cOne` is emitted
+// immediately after the second.
+// CHECK: arith.constant 0 : index
+// CHECK: arith.constant 0 : index
+// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
+
+// Fortran dim 0 (embox lb = 2) -> delta 1, added onto memref position 1.
+// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C2]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+
+// Fortran dim 1 (embox lb = 1) -> delta 0, added onto memref position 0.
+// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %{{.+}}, %[[LB1_DELTA]] : index
+
+// reinterpret_cast: offset stays a plain 0 constant, sizes = slice extents
+// (1, 1), strides = [parent's dim-0 element stride, 1] in memref order.
+// CHECK-NEXT: memref.reinterpret_cast %[[MEMREF]] to offset: [%{{.+}}], sizes: [%[[C1]], %[[C1]]], strides: [%[[C4]], %{{.+}}] : memref<2x4xi32> to memref<?x?xi32, strided<[?, ?], offset: ?>>
+
+// memref.load consumes the shifted indices in memref order
+// (dim 1 outer -> IDX_DIM1, dim 0 inner -> IDX_DIM0).
+// CHECK-NEXT: memref.load %{{.+}}[%[[IDX_DIM1]], %[[IDX_DIM0]]] : memref<?x?xi32, strided<[?, ?], offset: ?>>
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
new file mode 100644
index 0000000000000..7c98a6c6bac1a
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
@@ -0,0 +1,82 @@
+// RUN: fir-opt %s --fir-to-memref | FileCheck %s
+
+// When the fir.embox slice has a rank-reducing scalar-subscript triple
+// (undef ub/step), the corresponding parent Fortran dim is collapsed in the
+// box's rank but still has a memref index position under the reinterpret_cast
+// (getRankFromEmbox returns the parent rank). The fold OVERWRITES that index
+// with (lb - 1) instead of adding to it; the memref stride carried by that
+// slot times (lb - 1) reaches the correct parent element, without needing to
+// touch the reinterpret_cast's flat offset.
+
+func.func @rank_reduced_embox_slice(
+ %arg0: !fir.ref<!fir.array<3x2x2xi32>>, %i: index, %j: index) -> i32 {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c3 = arith.constant 3 : index
+ %undef = fir.undefined index
+ %shape = fir.shape %c3, %c2, %c2 : (index, index, index) -> !fir.shape<3>
+ // Embox slice: a(:, 2, :) -- range/scalar/range. Collapses parent dim 1
+ // (Fortran dim 1) at lb = 2. Result is a rank-2 fir.box.
+ %eslice = fir.slice %c1, %c3, %c1, %c2, %undef, %undef, %c1, %c2, %c1
+ : (index, index, index, index, index, index, index, index, index)
+ -> !fir.slice<3>
+ %box = fir.embox %arg0(%shape) [%eslice]
+ : (!fir.ref<!fir.array<3x2x2xi32>>, !fir.shape<3>, !fir.slice<3>)
+ -> !fir.box<!fir.array<3x2xi32>>
+ // Array_coor with its own rank-2 slice on the box + 2 indices.
+ %ashape = fir.shape %c3, %c2 : (index, index) -> !fir.shape<2>
+ %ai = arith.index_cast %i : index to i64
+ %islice = fir.slice %c1, %c3, %c1, %j, %undef, %undef
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %addr = fir.array_coor %box(%ashape) [%islice] %ai, %j
+ : (!fir.box<!fir.array<3x2xi32>>, !fir.shape<2>, !fir.slice<2>, i64, index)
+ -> !fir.ref<i32>
+ %v = fir.load %addr : !fir.ref<i32>
+ return %v : i32
+}
+
+// CHECK-LABEL: func.func @rank_reduced_embox_slice(
+// CHECK-SAME: %[[ARG0:.+]]: !fir.ref<!fir.array<3x2x2xi32>>,
+// CHECK-SAME: %[[I:.+]]: index, %[[J:.+]]: index) -> i32
+// CHECK-DAG: %[[C1:.+]] = arith.constant 1 : index
+// CHECK-DAG: %[[C2:.+]] = arith.constant 2 : index
+// CHECK-DAG: %[[C3:.+]] = arith.constant 3 : index
+// CHECK: %[[UNDEF:.+]] = fir.undefined index
+
+// Parent (rank-3) fir.shape and rank-reducing fir.slice survive.
+// CHECK: %[[SHAPE:.+]] = fir.shape %[[C3]], %[[C2]], %[[C2]] : (index, index, index) -> !fir.shape<3>
+// CHECK: %[[ESLICE:.+]] = fir.slice %[[C1]], %[[C3]], %[[C1]], %[[C2]], %[[UNDEF]], %[[UNDEF]], %[[C1]], %[[C2]], %[[C1]] : ({{.+}}) -> !fir.slice<3>
+// CHECK: fir.embox %[[ARG0]](%[[SHAPE]]) [%[[ESLICE]]] : ({{.+}}) -> !fir.box<!fir.array<3x2xi32>>
+
+// Row-major memref view of the parent (col-major !fir.array<3x2x2>).
+// CHECK: %[[MEMREF:.+]] = fir.convert %[[ARG0]] : (!fir.ref<!fir.array<3x2x2xi32>>) -> memref<2x2x3xi32>
+
+// Anchor via the two `arith.constant 0 : index` uses (getMemrefIndices' zero
+// placeholder, then reinterpret_cast's offset) -- the fold's `cOne` is
+// emitted immediately after the second.
+// CHECK: arith.constant 0 : index
+// CHECK: arith.constant 0 : index
+// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
+
+// Fortran dim 0 (embox slice range, lb = 1) -> delta 0 added onto memref
+// position 2.
+// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+
+// Fortran dim 1 (embox slice SCALAR, lb = 2) -> delta 1 OVERWRITES memref
+// position 1 (no arith.addi follows -- the delta itself becomes the index).
+// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C2]], %[[CONE]] : index
+
+// Fortran dim 2 (embox slice range, lb = 1) -> delta 0 added onto memref
+// position 0.
+// CHECK-NEXT: %[[LB2_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM2:.+]] = arith.addi %{{.+}}, %[[LB2_DELTA]] : index
+
+// reinterpret_cast: offset stays 0 (never touched), sizes = parent extents in
+// memref order (3, 2, 3) via getMemrefIndices' rank override, strides =
+// parent's col-major element strides in memref order (2*3, 3, 1).
+// CHECK-NEXT: memref.reinterpret_cast %[[MEMREF]] to offset: [%{{.+}}], sizes: [%[[C3]], %[[C2]], %[[C3]]], strides: [%{{.+}}, %[[C3]], %{{.+}}] : memref<2x2x3xi32> to memref<?x?x?xi32, strided<[?, ?, ?], offset: ?>>
+
+// memref.load: memref-order indices. Position 1 (collapsed dim) is
+// LB1_DELTA directly (not through arith.addi).
+// CHECK-NEXT: memref.load %{{.+}}[%[[IDX_DIM2]], %[[LB1_DELTA]], %[[IDX_DIM0]]] : memref<?x?x?xi32, strided<[?, ?, ?], offset: ?>>
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
new file mode 100644
index 0000000000000..8a283fd52d570
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
@@ -0,0 +1,67 @@
+// RUN: fir-opt %s --fir-to-memref | FileCheck %s
+
+// When both the embox and its array_coor consumer carry a slice, FIRToMemRef
+// must synthesize reinterpret_cast strides from the *parent's* extents, not
+// the box's (= slice's). For a slice that keeps > 1 element per dim, using
+// the slice extents makes the outer stride walk by the slice's size instead
+// of the parent's leading dim, so memref.load reads across the wrong columns.
+
+func.func @emboxed_slice_stride(%arg0: !fir.ref<!fir.array<4x2xi32>>) -> i32 {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c4 = arith.constant 4 : index
+ %ci = arith.constant 1 : i64
+ %shape = fir.shape %c4, %c2 : (index, index) -> !fir.shape<2>
+ // Embox slice: a(1:2, :) -- keeps 2 elements per dim (all steps 1, all lb 1).
+ %eslice = fir.slice %c1, %c2, %c1, %c1, %c2, %c1
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %box = fir.embox %arg0(%shape) [%eslice]
+ : (!fir.ref<!fir.array<4x2xi32>>, !fir.shape<2>, !fir.slice<2>)
+ -> !fir.box<!fir.array<2x2xi32>>
+ // Inner array_coor slice (identity 1:1:1 in both dims).
+ %ashape = fir.shape %c2, %c2 : (index, index) -> !fir.shape<2>
+ %islice = fir.slice %c1, %c1, %c1, %c1, %c1, %c1
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %addr = fir.array_coor %box(%ashape) [%islice] %ci, %c1
+ : (!fir.box<!fir.array<2x2xi32>>, !fir.shape<2>, !fir.slice<2>, i64, index)
+ -> !fir.ref<i32>
+ %v = fir.load %addr : !fir.ref<i32>
+ return %v : i32
+}
+
+// CHECK-LABEL: func.func @emboxed_slice_stride(
+// CHECK-SAME: %[[ARG0:.+]]: !fir.ref<!fir.array<4x2xi32>>) -> i32
+// CHECK-DAG: %[[C1:.+]] = arith.constant 1 : index
+// CHECK-DAG: %[[C2:.+]] = arith.constant 2 : index
+// CHECK-DAG: %[[C4:.+]] = arith.constant 4 : index
+
+// Parent fir.shape and embox fir.slice survive.
+// CHECK: %[[SHAPE:.+]] = fir.shape %[[C4]], %[[C2]] : (index, index) -> !fir.shape<2>
+// CHECK: %[[ESLICE:.+]] = fir.slice %[[C1]], %[[C2]], %[[C1]], %[[C1]], %[[C2]], %[[C1]] : ({{.+}}) -> !fir.slice<2>
+// CHECK: fir.embox %[[ARG0]](%[[SHAPE]]) [%[[ESLICE]]] : ({{.+}}) -> !fir.box<!fir.array<2x2xi32>>
+
+// Row-major memref view of the parent (col-major !fir.array<4x2>).
+// CHECK: %[[MEMREF:.+]] = fir.convert %[[ARG0]] : (!fir.ref<!fir.array<4x2xi32>>) -> memref<2x4xi32>
+
+// Anchor via the two `arith.constant 0 : index` uses (getMemrefIndices' zero
+// placeholder, then reinterpret_cast's offset) -- the fold's `cOne` is
+// emitted immediately after the second.
+// CHECK: arith.constant 0 : index
+// CHECK: arith.constant 0 : index
+// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
+
+// Fortran dim 0 (embox lb = 1) -> delta 0 added onto memref position 1.
+// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+
+// Fortran dim 1 (embox lb = 1) -> delta 0 added onto memref position 0.
+// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
+// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %{{.+}}, %[[LB1_DELTA]] : index
+
+// The stride check is the point of the test: sizes = slice extents (2, 2),
+// strides = [parent's dim-0 element stride = 4, 1] in memref order. The
+// outer stride MUST be %c4 (parent dim 0), not %c2 (slice's own extent).
+// CHECK-NEXT: memref.reinterpret_cast %[[MEMREF]] to offset: [%{{.+}}], sizes: [%[[C2]], %[[C2]]], strides: [%[[C4]], %{{.+}}] : memref<2x4xi32> to memref<?x?xi32, strided<[?, ?], offset: ?>>
+
+// memref.load consumes the shifted indices in memref order.
+// CHECK-NEXT: memref.load %{{.+}}[%[[IDX_DIM1]], %[[IDX_DIM0]]] : memref<?x?xi32, strided<[?, ?], offset: ?>>
>From 98a83dacefb3c2c7144982784dcefdf8ca54604d Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Tue, 7 Jul 2026 02:22:31 -0700
Subject: [PATCH 2/2] Review comments: non-unit stride
---
.../lib/Optimizer/Transforms/FIRToMemRef.cpp | 103 +++++++++++-------
.../FIRToMemRef/emboxed-slice-array-coor.mlir | 12 +-
...xed-slice-non-unit-stride-non-unit-lb.mlir | 69 ++++++++++++
...mboxed-slice-rank-reduction-nonprefix.mlir | 58 ++++++++++
.../emboxed-slice-rank-reduction.mlir | 17 +--
.../FIRToMemRef/emboxed-slice-stride.mlir | 12 +-
6 files changed, 216 insertions(+), 55 deletions(-)
create mode 100644 flang/test/Transforms/FIRToMemRef/emboxed-slice-non-unit-stride-non-unit-lb.mlir
create mode 100644 flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction-nonprefix.mlir
diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index d86d573ef63af..f503a5ef04588 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -460,24 +460,23 @@ static Value castTypeToIndexType(Value originalValue,
originalValue);
}
-/// Fold the embox's slice per-dim (lb - 1) shift into `indices` after
-/// `getMemrefIndices` has produced them. Handles both range and rank-reducing
-/// (scalar-subscript) slice triples uniformly.
+/// Fold the embox's slice per-dim (lb - 1) shift and stride into `indices`
+/// after `getMemrefIndices` has produced them. Handles both range and
+/// rank-reducing (scalar-subscript) slice triples uniformly.
///
/// Necessary because `getMemrefIndices` only consumes the first `rank*3`
/// entries of `sliceInfo.sliceVec` (the array_coor's own slice); the embox's
-/// triples sit at `sliceVec[rank*3 .. 2*rank*3-1]` and are otherwise dropped,
-/// leaving the memref access short by `(lb - 1)` per dim -- and, for
-/// collapsed dims, additionally polluted with the array_coor's own scalar
-/// subscript (typically a loop variable) that would drag the load off the
-/// target dim across iterations.
+/// triples sit at `sliceVec[rank*3 .. 2*rank*3-1]` and are otherwise dropped.
///
/// Behaviour, per Fortran dim `d` of the embox slice (memref position
/// `m = parentRank - 1 - d`):
-/// - Range triple `(lb, ub, step)`:
-/// indices[m] += (lb - 1)
-/// Adds the (lb - 1) shift on top of whatever `getMemrefIndices` produced
-/// for that dim.
+/// - Range triple `(lb, ub, stride)`:
+/// indices[m] = indices[m] * stride + (lb - 1)
+/// `indices[m]` on entry is a *0-based* box index produced by
+/// `getMemrefIndices`. Converting a 0-based box index j to a 0-based
+/// parent index via the embox range (lb, stride) gives:
+/// parent_0based = lb + j*stride - 1 = j*stride + (lb - 1)
+/// For unit stride this reduces to `indices[m] += (lb - 1)`.
/// - Scalar-subscript triple `(lb, undef, undef)`:
/// indices[m] = (lb - 1)
/// Overwrites the memref position outright. The dim is collapsed in the
@@ -487,20 +486,32 @@ static Value castTypeToIndexType(Value originalValue,
/// `(lb - 1) * parentStride[d]`, without needing any parent-shape
/// lookups from the caller.
///
-/// Example A -- non-rank-reducing (2-D):
+/// Example A -- non-rank-reducing (2-D), unit strides:
///
/// `fir.array_coor %box(...)[%innerSlice] %i, %j` with
/// - array_coor slice: identity in both dims
/// - embox slice : `fir.slice %c2,%c2,%c1, %c1,%c1,%c1`
-/// (Fortran dim 0 lb = 2, dim 1 lb = 1)
+/// (Fortran dim 0 lb = 2, stride = 1)
///
/// Incoming `indices` (memref order): [ %j', %i' ]
/// Fold:
-/// - dim 0 (range): delta = 1 -> indices[1] += 1
-/// - dim 1 (range): delta = 0 -> indices[0] += 0
+/// - dim 0 (range, stride=1, lb=2): indices[1] = %i'*1 + 1
+/// - dim 1 (range, stride=1, lb=1): indices[0] = %j'*1 + 0
/// Result: [ %j', %i' + 1 ]
///
-/// Example B -- rank-reducing (3-D parent, dim 1 collapsed):
+/// Example B -- non-rank-reducing (1-D), non-unit stride (e.g. a(1:10:2)):
+///
+/// `fir.array_coor %box(...)[%innerSlice] %i` with
+/// - array_coor slice: identity
+/// - embox slice : `fir.slice %c1,%c10,%c2`
+/// (Fortran dim 0 lb = 1, stride = 2)
+///
+/// Incoming `indices`: [ %i' ] where %i' = i - 1 (from getMemrefIndices)
+/// Fold:
+/// - dim 0 (range, stride=2, lb=1): indices[0] = %i'*2 + 0
+/// Result: [ (i-1)*2 ]
+///
+/// Example C -- rank-reducing (3-D parent, dim 1 collapsed):
///
/// %parent : !fir.ref<!fir.array<3x2x2xi32>>
/// %shape = fir.shape %c3, %c2, %c2
@@ -518,9 +529,9 @@ static Value castTypeToIndexType(Value originalValue,
/// ^ dim 2 ^ dim 1 (collapsed) ^ dim 0
///
/// Fold:
-/// - dim 0 (range, lb=1): indices[2] += 0
+/// - dim 0 (range, stride=1, lb=1): indices[2] = %ii'*1 + 0
/// - dim 1 (scalar, lb=2): indices[1] = 1 (overwrite)
-/// - dim 2 (range, lb=1): indices[0] += 0
+/// - dim 2 (range, stride=1, lb=1): indices[0] = %jj'*1 + 0
///
/// Final: `memref.load %reinterpret_cast[%jj', 1, %ii'] : memref<?x?x?xi32,
/// strided<[?, ?, ?], offset: ?>>` -- stride 3 on the middle position
@@ -548,9 +559,17 @@ void FIRToMemRef::foldSliceLbIntoIndices(SmallVectorImpl<Value> &indices,
Value lb = castTypeToIndexType(triples[d * 3], rewriter);
Value delta = arith::SubIOp::create(rewriter, loc, lb, cOne);
bool isScalar = isUndef(triples[d * 3 + 1]) || isUndef(triples[d * 3 + 2]);
- indices[m] = isScalar
- ? delta
- : arith::AddIOp::create(rewriter, loc, indices[m], delta);
+ if (isScalar) {
+ indices[m] = delta;
+ } else {
+ // Scale the box-relative index by the embox stride before adding the lb
+ // offset. For non-unit strides (e.g. a(1:10:2)) omitting this would give
+ // the wrong physical element.
+ Value strideVal = castTypeToIndexType(triples[d * 3 + 2], rewriter);
+ Value scaled =
+ arith::MulIOp::create(rewriter, loc, indices[m], strideVal);
+ indices[m] = arith::AddIOp::create(rewriter, loc, scaled, delta);
+ }
}
}
@@ -846,7 +865,8 @@ FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp,
if (!complexPartIdx && memRefTy.hasStaticShape() && !isDescriptor)
return std::pair{*converted, indices};
- unsigned rank = arrayCoorOp.getIndices().size();
+ const unsigned acRank = arrayCoorOp.getIndices().size();
+ unsigned rank = acRank;
if (auto embox = firMemref.getDefiningOp<fir::EmboxOp>())
rank = getRankFromEmbox(embox);
@@ -913,13 +933,16 @@ FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp,
Value oneIdx =
arith::ConstantIndexOp::create(rewriter, arrayCoorOp->getLoc(), 1);
// shapeVec is populated by collectSliceInfoFrom in the order:
- // [<arrayCoor's shape>, <embox/rebox's shape>]
- // When both contribute (firMemrefIsEmbox && arrayCoorOp has a slice), the
- // first `rank` entries are the *box's* shape (= slice extents) while the
- // next `rank` are the underlying *parent's* extents. Strides for the
- // reinterpret_cast must come from the parent's contiguous element strides,
- // not the slice extents (using the latter yields an outer stride that
- // walks by the slice's size instead of the parent's leading dim).
+ // [<arrayCoor's shape (acRank entries)>, <embox's shape (rank entries)>]
+ // When both contribute (firMemrefIsEmbox && arrayCoorOp has a slice),
+ // shapeVec[0..acRank-1] are the box's visible extents and
+ // shapeVec[acRank..acRank+rank-1] are the parent's extents. Strides for
+ // the reinterpret_cast must come from the parent's contiguous element
+ // strides, not the box's extents (using the latter yields an outer stride
+ // that walks by the slice's size instead of the parent's leading dim).
+ //
+ // acRank == rank in the non-rank-reducing case; acRank < rank when the
+ // embox slice collapses one or more dims via scalar subscripts.
//
// Example -- 4x2 parent, slice a(1:2, :) keeping 2 elements per dim:
// %parent : !fir.ref<!fir.array<4x2xi32>>
@@ -937,27 +960,27 @@ FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp,
//
// For rank = 2 the loop below emits (memref order, outer -> inner):
// size[outer] = shapeVec[1] = 2 (slice's dim1)
- // stride[outer] = shapeVec[parentShapeBase + 0] = 4 (parent's dim0)
- // size[inner] = shapeVec[0] = 2 (slice's dim0)
- // stride[inner] = 1
+ // stride[outer] = shapeVec[parentShapeStartIdx + 0] = 4 (parent's
+ // dim0) size[inner] = shapeVec[0] = 2 (slice's
+ // dim0) stride[inner] = 1
//
- // Without the `parentShapeBase` shift, `stride[outer]` would be
+ // Without the `parentShapeStartIdx` shift, `stride[outer]` would be
// `shapeVec[0] = 2` -- the slice's own extent -- and successive outer
// steps would walk by 2 elements instead of the parent's 4-element row,
// clobbering the wrong columns.
const bool hasParentShape = firMemrefIsEmbox && arrayCoorOp.getSlice() &&
- shapeVec.size() >= 2 * rank;
- const unsigned parentShapeBase = hasParentShape ? rank : 0;
+ shapeVec.size() >= acRank + rank;
+ const unsigned parentShapeStartIdx = hasParentShape ? acRank : 0;
for (unsigned i = rank - 1; i > 0; --i) {
// Sizes are always the box/slice's visible extents (shapeVec[0..rank-1]).
Value size = shapeVec[i];
sizes.push_back(castTypeToIndexType(size, rewriter));
- // Strides use the parent's extents (via `parentShapeBase`).
- Value stride = shapeVec[parentShapeBase + 0];
+ // Strides use the parent's extents (via `parentShapeStartIdx`).
+ Value stride = shapeVec[parentShapeStartIdx + 0];
for (unsigned j = 1; j <= i - 1; ++j)
- stride = arith::MulIOp::create(rewriter, loc,
- shapeVec[parentShapeBase + j], stride);
+ stride = arith::MulIOp::create(
+ rewriter, loc, shapeVec[parentShapeStartIdx + j], stride);
if (complexPartIdx)
stride = arith::MulIOp::create(
rewriter, loc, stride,
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
index 236b8f6a97dea..62235c94680a2 100644
--- a/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-array-coor.mlir
@@ -48,13 +48,17 @@ func.func @emboxed_slice_array_coor(%arg0: !fir.ref<!fir.array<4x2xi32>>) -> i32
// CHECK: arith.constant 0 : index
// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
-// Fortran dim 0 (embox lb = 2) -> delta 1, added onto memref position 1.
+// Fortran dim 0 (embox lb = 2, stride = 1) -> delta 1, added onto memref
+// position 1.
// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C2]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+// CHECK-NEXT: %[[SCALED0:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %[[SCALED0]], %[[LB0_DELTA]] : index
-// Fortran dim 1 (embox lb = 1) -> delta 0, added onto memref position 0.
+// Fortran dim 1 (embox lb = 1, stride = 1) -> delta 0, added onto memref
+// position 0.
// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %{{.+}}, %[[LB1_DELTA]] : index
+// CHECK-NEXT: %[[SCALED1:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %[[SCALED1]], %[[LB1_DELTA]] : index
// reinterpret_cast: offset stays a plain 0 constant, sizes = slice extents
// (1, 1), strides = [parent's dim-0 element stride, 1] in memref order.
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-non-unit-stride-non-unit-lb.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-non-unit-stride-non-unit-lb.mlir
new file mode 100644
index 0000000000000..cc32122095834
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-non-unit-stride-non-unit-lb.mlir
@@ -0,0 +1,69 @@
+// RUN: fir-opt %s --fir-to-memref | FileCheck %s
+
+// Verify that when BOTH the embox slice and the inner array_coor slice have
+// non-unit lower bounds and non-unit strides, the correct physical index is
+// produced. All four values (embox lb, embox stride, ac lb, ac stride) are
+// intentionally distinct to catch any mix-up between them.
+//
+// Embox slice: a(3:20:2) -- lb=3, stride=2, 9 elements: 3,5,7,...,19
+// AC slice: 4:9:5 -- lb=4, stride=5, 2 positions in box: 4,9
+// Index %i (1-based into ac slice):
+// getMemrefIndices: inner = (i-1)*5 + (4-1) = (i-1)*5 + 3
+// foldSliceLbIntoIndices: physical = inner*2 + (3-1) = inner*2 + 2
+//
+// For i=1: inner=3, physical=8 (0-based) -> a(9) = a(3 + 3*2). ok
+// For i=2: inner=8, physical=18 (0-based) -> a(19) = a(3 + 8*2). ok
+
+func.func @emboxed_slice_nonstride_nonlb(
+ %arg0: !fir.ref<!fir.array<20xi32>>, %i: index) -> i32 {
+ %c2 = arith.constant 2 : index
+ %c3 = arith.constant 3 : index
+ %c4 = arith.constant 4 : index
+ %c5 = arith.constant 5 : index
+ %c9 = arith.constant 9 : index
+ %c20 = arith.constant 20 : index
+ %shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ // Embox slice: a(3:20:2) -- lb=3, stride=2, 9 elements.
+ %eslice = fir.slice %c3, %c20, %c2 : (index, index, index) -> !fir.slice<1>
+ %box = fir.embox %arg0(%shape) [%eslice]
+ : (!fir.ref<!fir.array<20xi32>>, !fir.shape<1>, !fir.slice<1>)
+ -> !fir.box<!fir.array<9xi32>>
+ // Inner array_coor slice: 4:9:5 -- lb=4, stride=5.
+ %ashape = fir.shape %c9 : (index) -> !fir.shape<1>
+ %islice = fir.slice %c4, %c9, %c5 : (index, index, index) -> !fir.slice<1>
+ %addr = fir.array_coor %box(%ashape) [%islice] %i
+ : (!fir.box<!fir.array<9xi32>>, !fir.shape<1>, !fir.slice<1>, index)
+ -> !fir.ref<i32>
+ %v = fir.load %addr : !fir.ref<i32>
+ return %v : i32
+}
+
+// CHECK-LABEL: func.func @emboxed_slice_nonstride_nonlb(
+// CHECK-SAME: %[[ARG0:.+]]: !fir.ref<!fir.array<20xi32>>,
+// CHECK-SAME: %[[I:.+]]: index) -> i32
+// CHECK-DAG: %[[C2:.+]] = arith.constant 2 : index
+// CHECK-DAG: %[[C3:.+]] = arith.constant 3 : index
+// CHECK-DAG: %[[C5:.+]] = arith.constant 5 : index
+// CHECK-DAG: %[[C9:.+]] = arith.constant 9 : index
+
+// Row-major memref view of the parent.
+// CHECK: %[[MEMREF:.+]] = fir.convert %[[ARG0]] : (!fir.ref<!fir.array<20xi32>>) -> memref<20xi32>
+
+// Anchor: two `arith.constant 0 : index` -- getMemrefIndices' zero placeholder
+// and the reinterpret_cast offset. The fold's cOne is emitted right after.
+// CHECK: arith.constant 0 : index
+// CHECK: arith.constant 0 : index
+// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
+
+// foldSliceLbIntoIndices for embox slice (lb=3, stride=2):
+// delta = 3 - 1 = 2
+// physical = inner * 2 + 2
+// CHECK-NEXT: %[[LB_DELTA:.+]] = arith.subi %[[C3]], %[[CONE]] : index
+// CHECK-NEXT: %[[SCALED:.+]] = arith.muli %{{.+}}, %[[C2]] : index
+// CHECK-NEXT: %[[IDX:.+]] = arith.addi %[[SCALED]], %[[LB_DELTA]] : index
+
+// reinterpret_cast: contiguous parent layout, box extent as size.
+// CHECK-NEXT: memref.reinterpret_cast %[[MEMREF]] to offset: [%{{.+}}], sizes: [%[[C9]]], strides: [%{{.+}}] : memref<20xi32> to memref<?xi32, strided<[?], offset: ?>>
+
+// memref.load uses the fully-adjusted physical index.
+// CHECK-NEXT: memref.load %{{.+}}[%[[IDX]]] : memref<?xi32, strided<[?], offset: ?>>
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction-nonprefix.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction-nonprefix.mlir
new file mode 100644
index 0000000000000..ea8206da6bad3
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction-nonprefix.mlir
@@ -0,0 +1,58 @@
+// RUN: fir-opt %s --fir-to-memref | FileCheck %s
+
+// Rank-reducing embox slice where the box's visible extents do NOT share a
+// prefix with the parent's extents. Parent is 4x3x2 (Fortran col-major),
+// dim 1 is collapsed at lb=2, leaving box rank 2 with extents [4, 2].
+//
+// The parent's dim-1 extent is 3 while the box's dim-1 extent is 2, so if
+// the reinterpret_cast outer stride is accidentally built from box extents
+// instead of parent extents it becomes 4*2=8 instead of the correct 4*3=12.
+//
+// The hasParentShape/parentShapeBase logic must use acRank (= the array_coor
+// index count = box's visible rank = 2) as the base into shapeVec, NOT the
+// parent rank (= 3). With rank reduction acRank < rank, so the old
+// `shapeVec.size() >= 2*rank` guard would be false and parentShapeBase would
+// fall back to 0, mixing box and parent extents in the stride product.
+
+func.func @rank_reduced_nonprefix(
+ %arg0: !fir.ref<!fir.array<4x3x2xi32>>, %i: index, %j: index) -> i32 {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c3 = arith.constant 3 : index
+ %c4 = arith.constant 4 : index
+ %undef = fir.undefined index
+ %shape = fir.shape %c4, %c3, %c2 : (index, index, index) -> !fir.shape<3>
+ // Embox slice: a(:, 2, :) -- collapse parent dim 1 at lb=2.
+ %eslice = fir.slice %c1, %c4, %c1, %c2, %undef, %undef, %c1, %c2, %c1
+ : (index, index, index, index, index, index, index, index, index)
+ -> !fir.slice<3>
+ %box = fir.embox %arg0(%shape) [%eslice]
+ : (!fir.ref<!fir.array<4x3x2xi32>>, !fir.shape<3>, !fir.slice<3>)
+ -> !fir.box<!fir.array<4x2xi32>>
+ %ashape = fir.shape %c4, %c2 : (index, index) -> !fir.shape<2>
+ %ai = arith.index_cast %i : index to i64
+ %islice = fir.slice %c1, %c4, %c1, %j, %undef, %undef
+ : (index, index, index, index, index, index) -> !fir.slice<2>
+ %addr = fir.array_coor %box(%ashape) [%islice] %ai, %j
+ : (!fir.box<!fir.array<4x2xi32>>, !fir.shape<2>, !fir.slice<2>, i64, index)
+ -> !fir.ref<i32>
+ %v = fir.load %addr : !fir.ref<i32>
+ return %v : i32
+}
+
+// CHECK-LABEL: func.func @rank_reduced_nonprefix(
+// CHECK-SAME: %[[ARG0:.+]]: !fir.ref<!fir.array<4x3x2xi32>>,
+// CHECK-SAME: %[[I:.+]]: index, %[[J:.+]]: index) -> i32
+// CHECK-DAG: %[[C2:.+]] = arith.constant 2 : index
+// CHECK-DAG: %[[C3:.+]] = arith.constant 3 : index
+// CHECK-DAG: %[[C4:.+]] = arith.constant 4 : index
+
+// Row-major memref view of the parent (col-major !fir.array<4x3x2>).
+// CHECK: %[[MEMREF:.+]] = fir.convert %[[ARG0]] : (!fir.ref<!fir.array<4x3x2xi32>>) -> memref<2x3x4xi32>
+
+// The outer stride must use the parent's dim-1 extent (%c3), not the box's
+// dim-1 extent (%c2): correct = %c3*%c4 = 12, wrong = %c2*%c4 = 8.
+// CHECK: %[[OUTER_STRIDE:.+]] = arith.muli %[[C3]], %[[C4]] : index
+
+// Middle stride is parent's dim-0 extent (%c4); inner is 1.
+// CHECK: memref.reinterpret_cast %[[MEMREF]] to offset: [%{{.+}}], sizes: [%{{.+}}, %{{.+}}, %{{.+}}], strides: [%[[OUTER_STRIDE]], %[[C4]], %{{.+}}] : memref<2x3x4xi32> to memref<?x?x?xi32, strided<[?, ?, ?], offset: ?>>
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
index 7c98a6c6bac1a..474f0fc6c2e3b 100644
--- a/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-rank-reduction.mlir
@@ -58,19 +58,22 @@ func.func @rank_reduced_embox_slice(
// CHECK: arith.constant 0 : index
// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
-// Fortran dim 0 (embox slice range, lb = 1) -> delta 0 added onto memref
-// position 2.
+// Fortran dim 0 (embox slice range, lb = 1, stride = 1) -> delta 0 added onto
+// memref position 2.
// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+// CHECK-NEXT: %[[SCALED0:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %[[SCALED0]], %[[LB0_DELTA]] : index
// Fortran dim 1 (embox slice SCALAR, lb = 2) -> delta 1 OVERWRITES memref
-// position 1 (no arith.addi follows -- the delta itself becomes the index).
+// position 1 (no arith.muli or arith.addi -- the delta itself becomes the
+// index).
// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C2]], %[[CONE]] : index
-// Fortran dim 2 (embox slice range, lb = 1) -> delta 0 added onto memref
-// position 0.
+// Fortran dim 2 (embox slice range, lb = 1, stride = 1) -> delta 0 added onto
+// memref position 0.
// CHECK-NEXT: %[[LB2_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM2:.+]] = arith.addi %{{.+}}, %[[LB2_DELTA]] : index
+// CHECK-NEXT: %[[SCALED2:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM2:.+]] = arith.addi %[[SCALED2]], %[[LB2_DELTA]] : index
// reinterpret_cast: offset stays 0 (never touched), sizes = parent extents in
// memref order (3, 2, 3) via getMemrefIndices' rank override, strides =
diff --git a/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir b/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
index 8a283fd52d570..9ba9c269440ea 100644
--- a/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
+++ b/flang/test/Transforms/FIRToMemRef/emboxed-slice-stride.mlir
@@ -50,13 +50,17 @@ func.func @emboxed_slice_stride(%arg0: !fir.ref<!fir.array<4x2xi32>>) -> i32 {
// CHECK: arith.constant 0 : index
// CHECK-NEXT: %[[CONE:.+]] = arith.constant 1 : index
-// Fortran dim 0 (embox lb = 1) -> delta 0 added onto memref position 1.
+// Fortran dim 0 (embox lb = 1, stride = 1) -> delta 0 added onto memref
+// position 1.
// CHECK-NEXT: %[[LB0_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %{{.+}}, %[[LB0_DELTA]] : index
+// CHECK-NEXT: %[[SCALED0:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM0:.+]] = arith.addi %[[SCALED0]], %[[LB0_DELTA]] : index
-// Fortran dim 1 (embox lb = 1) -> delta 0 added onto memref position 0.
+// Fortran dim 1 (embox lb = 1, stride = 1) -> delta 0 added onto memref
+// position 0.
// CHECK-NEXT: %[[LB1_DELTA:.+]] = arith.subi %[[C1]], %[[CONE]] : index
-// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %{{.+}}, %[[LB1_DELTA]] : index
+// CHECK-NEXT: %[[SCALED1:.+]] = arith.muli %{{.+}}, %[[C1]] : index
+// CHECK-NEXT: %[[IDX_DIM1:.+]] = arith.addi %[[SCALED1]], %[[LB1_DELTA]] : index
// The stride check is the point of the test: sizes = slice extents (2, 2),
// strides = [parent's dim-0 element stride = 4, 1] in memref order. The
More information about the flang-commits
mailing list