[flang-commits] [flang] [FIRToMemRef] Fix wrong-code for array_coor over sliced fir.embox (PR #207749)
Kareem Ergawy via flang-commits
flang-commits at lists.llvm.org
Mon Jul 6 08:04:15 PDT 2026
https://github.com/ergawy created https://github.com/llvm/llvm-project/pull/207749
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.
>From eab33518be4b670499808c15e4773f143c240f41 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] [FIRToMemRef] Fix wrong-code for 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 | 151 +++++++++++++++++-
.../FIRToMemRef/emboxed-slice-array-coor.mlir | 65 ++++++++
.../emboxed-slice-rank-reduction.mlir | 82 ++++++++++
.../FIRToMemRef/emboxed-slice-stride.mlir | 67 ++++++++
4 files changed, 363 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..ff844d7b29978 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,101 @@ 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 +913,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 +998,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: ?>>
More information about the flang-commits
mailing list