[flang-commits] [flang] [flang][FIRToMemRef] lower `fir.coordinate_of` on static-extent arrays to indexed memref (PR #195404)
via flang-commits
flang-commits at lists.llvm.org
Fri May 1 19:49:15 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Susan Tan (ス-ザン タン) (SusanTan)
<details>
<summary>Changes</summary>
`fir.coordinate_of` on static array components (e.g. A%v(i)) was falling back to a rank-0 scalar memref, losing index information and blocking saffine parallelization. Lower these to a properly-shaped memref<dims x T> with explicit indices. Dynamic arrays and struct-element arrays keep the existing scalar fallback.
---
Full diff: https://github.com/llvm/llvm-project/pull/195404.diff
2 Files Affected:
- (modified) flang/lib/Optimizer/Transforms/FIRToMemRef.cpp (+107)
- (added) flang/test/Transforms/FIRToMemRef/coordinate-of.mlir (+97)
``````````diff
diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index ec58d6f3f1447..b78cf9c6d305b 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -129,6 +129,18 @@ class FIRToMemRef : public fir::impl::FIRToMemRefBase<FIRToMemRef> {
MemRefInfo convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp,
PatternRewriter &, FIRToMemRefTypeConverter &);
+ /// Returns true if \p coordinateOp can be lowered to an indexed memref access
+ /// by convertCoordinateArrayOp. This is true when the base is a reference to
+ /// a statically-shaped scalar array.
+ bool isArrayIndexingCoordinateOp(fir::CoordinateOp coordinateOp,
+ FIRToMemRefTypeConverter &) const;
+
+ /// Lower a fir.coordinate_of that indexes into a static-extent scalar array
+ /// (e.g. a struct component like `A%v(i)`) to a memref + index pair.
+ MemRefInfo convertCoordinateArrayOp(Operation *memOp, fir::CoordinateOp,
+ PatternRewriter &,
+ FIRToMemRefTypeConverter &);
+
void replaceFIRMemrefs(Value, Value, PatternRewriter &) const;
FailureOr<Value> getFIRConvert(Operation *memOp, Operation *memref,
@@ -867,6 +879,75 @@ Value FIRToMemRef::canonicalizeIndex(Value index,
return index;
}
+bool FIRToMemRef::isArrayIndexingCoordinateOp(
+ fir::CoordinateOp coordinateOp,
+ FIRToMemRefTypeConverter &typeConverter) const {
+ // The base must be a reference/pointer/heap to a sequence/array type.
+ Type baseType = coordinateOp.getRef().getType();
+ Type unwrapped = fir::dyn_cast_ptrEleTy(baseType);
+ if (!unwrapped)
+ return false;
+
+ auto seqTy = dyn_cast<fir::SequenceType>(unwrapped);
+ if (!seqTy)
+ return false;
+
+ // Restrict to fully static extents — dynamic arrays would need a shape
+ // operand (which coordinate_of lacks) to build a valid memref descriptor.
+ if (fir::hasDynamicSize(seqTy))
+ return false;
+
+ // The element type must be a convertible scalar — no derived types.
+ if (!typeConverter.convertibleMemrefType(baseType))
+ return false;
+
+ return true;
+}
+
+MemRefInfo FIRToMemRef::convertCoordinateArrayOp(
+ Operation *memOp, fir::CoordinateOp coordinateOp, PatternRewriter &rewriter,
+ FIRToMemRefTypeConverter &typeConverter) {
+ Value firBase = coordinateOp.getRef();
+ Location loc = coordinateOp->getLoc();
+ IndexType indexTy = rewriter.getIndexType();
+
+ if (typeConverter.isEmptyArray(firBase.getType()))
+ return failure();
+
+ // Convert the base ref/heap/ptr to a memref. convertMemrefType reverses the
+ // FIR column-major shape to row-major, keeping it in sync with index reversal
+ // below.
+ rewriter.setInsertionPoint(coordinateOp);
+ FailureOr<Value> converted;
+ if (isa<BlockArgument>(firBase)) {
+ Type memrefTy = typeConverter.convertMemrefType(firBase.getType());
+ converted =
+ fir::ConvertOp::create(rewriter, loc, memrefTy, firBase).getResult();
+ } else {
+ converted =
+ getFIRConvert(memOp, firBase.getDefiningOp(), rewriter, typeConverter);
+ if (failed(converted))
+ return failure();
+ }
+ rewriter.setInsertionPointAfter(coordinateOp);
+
+ // The converted memref has static shape — no reinterpret_cast needed.
+ assert(cast<MemRefType>(converted->getType()).hasStaticShape() &&
+ "expected static shape for coordinate_of array base");
+
+ // Collect and normalize the 0-based coor indices
+ SmallVector<Value> indices;
+ for (Value v : coordinateOp.getCoor()) {
+ v = canonicalizeIndex(v, rewriter);
+ if (!isa<IndexType>(v.getType()))
+ v = arith::IndexCastOp::create(rewriter, loc, indexTy, v);
+ indices.push_back(v);
+ }
+ std::reverse(indices.begin(), indices.end());
+
+ return std::pair{*converted, indices};
+}
+
MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
PatternRewriter &rewriter,
FIRToMemRefTypeConverter &typeConverter,
@@ -937,6 +1018,32 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
}
if (auto coordinateOp = dyn_cast<fir::CoordinateOp>(memrefOp)) {
+ // Fast path: coordinate_of used as a plain array indexer on a static-extent
+ // scalar array (e.g. a struct component `A%v(i)`). Convert the base to a
+ // proper indexed memref so saffine can analyse loop-independence.
+ if (isArrayIndexingCoordinateOp(coordinateOp, typeConverter)) {
+ MemRefInfo memrefInfo = convertCoordinateArrayOp(memOp, coordinateOp,
+ rewriter, typeConverter);
+ if (succeeded(memrefInfo)) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "FIRToMemRef: converted coordinate_of array indexer\n");
+ for (auto user : memrefOp->getUsers()) {
+ if (!isa<fir::LoadOp, fir::StoreOp>(user)) {
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "FIRToMemRef: coordinate_of used by non-load/store, "
+ "skipping erase\n";
+ firMemref.dump(); user->dump());
+ return memrefInfo;
+ }
+ }
+ eraseOps.insert(memrefOp);
+ return memrefInfo;
+ }
+ }
+
+ // Fallback: struct field access or dynamic array — produce a rank-0 scalar
+ // memref from the leaf reference (existing behaviour).
FailureOr<Value> converted =
getFIRConvert(memOp, coordinateOp, rewriter, typeConverter);
if (failed(converted)) {
diff --git a/flang/test/Transforms/FIRToMemRef/coordinate-of.mlir b/flang/test/Transforms/FIRToMemRef/coordinate-of.mlir
new file mode 100644
index 0000000000000..6b29551d4a3ab
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/coordinate-of.mlir
@@ -0,0 +1,97 @@
+// Tests for fir.coordinate_of lowering in FIRToMemRef.
+//
+// Accepted conversions: 1-D (block-argument base), 1-D (store),
+// 1-D (alloca-defined base via getFIRConvert), 2-D and 3-D (shape and
+// index reversal from FIR column-major to memref row-major).
+// Rejected conversions: dynamic-extent array (hasDynamicSize → falls back
+// to rank-0 scalar memref; fir.coordinate_of is NOT erased).
+//
+// RUN: fir-opt %s --fir-to-memref --allow-unregistered-dialect | FileCheck %s
+
+// ----------------------------------------------------------------------------
+// Accepted: 1-D load, block-argument base (fs28932c pattern).
+// The base is a region/function block argument so it is converted directly
+// via fir.convert; fir.coordinate_of is erased.
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_1d_load_block_arg
+// CHECK: [[BASE:%.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<3xf32>>) -> memref<3xf32>
+// CHECK: memref.load [[BASE]][%c1] : memref<3xf32>
+// CHECK-NOT: fir.coordinate_of
+func.func @coor_1d_load_block_arg(%arg0: !fir.ref<!fir.array<3xf32>>) {
+ %c1 = arith.constant 1 : index
+ %ptr = fir.coordinate_of %arg0, %c1 : (!fir.ref<!fir.array<3xf32>>, index) -> !fir.ref<f32>
+ %val = fir.load %ptr : !fir.ref<f32>
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Accepted: 1-D store, block-argument base.
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_1d_store_block_arg
+// CHECK: [[BASE:%.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<3xf32>>) -> memref<3xf32>
+// CHECK: memref.store {{%.+}}, [[BASE]][%c0] : memref<3xf32>
+// CHECK-NOT: fir.coordinate_of
+func.func @coor_1d_store_block_arg(%arg0: !fir.ref<!fir.array<3xf32>>) {
+ %c0 = arith.constant 0 : index
+ %cst = arith.constant 1.0 : f32
+ %ptr = fir.coordinate_of %arg0, %c0 : (!fir.ref<!fir.array<3xf32>>, index) -> !fir.ref<f32>
+ fir.store %cst to %ptr : !fir.ref<f32>
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Accepted: 1-D load, alloca-defined base (getFIRConvert path).
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_1d_load_alloca
+// CHECK: memref.alloca() : memref<3xf32>
+// CHECK: memref.load {{%.+}}[%c2] : memref<3xf32>
+// CHECK-NOT: fir.coordinate_of
+func.func @coor_1d_load_alloca() {
+ %c2 = arith.constant 2 : index
+ %alloc = fir.alloca !fir.array<3xf32>
+ %ptr = fir.coordinate_of %alloc, %c2 : (!fir.ref<!fir.array<3xf32>>, index) -> !fir.ref<f32>
+ %val = fir.load %ptr : !fir.ref<f32>
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Accepted: 2-D load — shape and index reversal.
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_2d_load
+// CHECK: [[BASE:%.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<2x3xf32>>) -> memref<3x2xf32>
+// CHECK: memref.load [[BASE]][%arg2, %arg1] : memref<3x2xf32>
+// CHECK-NOT: fir.coordinate_of
+func.func @coor_2d_load(%arg0: !fir.ref<!fir.array<2x3xf32>>, %arg1: index, %arg2: index) {
+ %ptr = fir.coordinate_of %arg0, %arg1, %arg2
+ : (!fir.ref<!fir.array<2x3xf32>>, index, index) -> !fir.ref<f32>
+ %val = fir.load %ptr : !fir.ref<f32>
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Accepted: 3-D load — shape and index reversal.
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_3d_load
+// CHECK: [[BASE:%.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<2x3x4xf32>>) -> memref<4x3x2xf32>
+// CHECK: memref.load [[BASE]][%arg3, %arg2, %arg1] : memref<4x3x2xf32>
+// CHECK-NOT: fir.coordinate_of
+func.func @coor_3d_load(%arg0: !fir.ref<!fir.array<2x3x4xf32>>,
+ %arg1: index, %arg2: index, %arg3: index) {
+ %ptr = fir.coordinate_of %arg0, %arg1, %arg2, %arg3
+ : (!fir.ref<!fir.array<2x3x4xf32>>, index, index, index) -> !fir.ref<f32>
+ %val = fir.load %ptr : !fir.ref<f32>
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Rejected: dynamic-extent array.
+// ----------------------------------------------------------------------------
+// CHECK-LABEL: func.func @coor_dynamic_reject
+// CHECK: [[COOR:%.+]] = fir.coordinate_of %arg0, %arg1
+// CHECK: [[CONV:%.+]] = fir.convert [[COOR]] : (!fir.ref<f32>) -> memref<f32>
+// CHECK: memref.load [[CONV]][] : memref<f32>
+func.func @coor_dynamic_reject(%arg0: !fir.ref<!fir.array<?xf32>>, %arg1: index) {
+ %ptr = fir.coordinate_of %arg0, %arg1 : (!fir.ref<!fir.array<?xf32>>, index) -> !fir.ref<f32>
+ %val = fir.load %ptr : !fir.ref<f32>
+ return
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/195404
More information about the flang-commits
mailing list