[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