[flang-commits] [flang] [flang][FIRToMemRef] Look through zero-offset views to find fir.array_coor (PR #215402)

Susan Tan ス-ザン タン via flang-commits flang-commits at lists.llvm.org
Mon Aug 10 15:03:23 PDT 2026


https://github.com/SusanTan updated https://github.com/llvm/llvm-project/pull/215402

>From 3ba77140d74fa8ac63a63a0367004027373c2dcf Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Mon, 10 Aug 2026 13:33:12 -0700
Subject: [PATCH 1/4] add impl

---
 .../lib/Optimizer/Transforms/FIRToMemRef.cpp  | 83 +++++++------------
 1 file changed, 32 insertions(+), 51 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 5851022b1bc47..7392516a9f377 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -101,6 +101,25 @@ static bool isMarshalLike(Operation *op) {
   return resIsMemRef || argIsMemRef;
 }
 
+/// Peel `FortranObjectViewOpInterface` ops with a statically-known zero
+/// offset (fir.declare, ref/box fir.convert, fir.box_addr,
+/// fir.volatile_cast, fir.create_box, unsliced fir.embox/fir.rebox) down to
+/// whatever produced the address underneath, e.g. fir.array_coor or a
+/// *sliced* embox/rebox (offset == nullopt, handled separately). Excludes a
+/// marshal-like fir.convert, which must go through its own dedicated path.
+static Value peelZeroOffsetViews(Value memref) {
+  while (Operation *defOp = memref.getDefiningOp()) {
+    auto view = dyn_cast<fir::FortranObjectViewOpInterface>(defOp);
+    if (!view || isMarshalLike(defOp))
+      break;
+    auto result = cast<OpResult>(memref);
+    if (view.getViewOffset(result) != 0)
+      break;
+    memref = view.getViewSource(result);
+  }
+  return memref;
+}
+
 using MemRefInfo = FailureOr<std::pair<Value, SmallVector<Value>>>;
 
 static llvm::cl::opt<bool> enableFIRConvertOptimizations(
@@ -144,7 +163,8 @@ class FIRToMemRef : public fir::impl::FIRToMemRefBase<FIRToMemRef> {
                                       PatternRewriter &,
                                       FIRToMemRefTypeConverter &);
 
-  void replaceFIRMemrefs(Value, Value, PatternRewriter &) const;
+  void replaceFIRMemrefs(Value, Value, ArrayRef<Value> indices,
+                         PatternRewriter &) const;
 
   FailureOr<Value> getFIRConvert(Operation *memOp, Operation *memref,
                                  PatternRewriter &, FIRToMemRefTypeConverter &);
@@ -1453,6 +1473,8 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
                                       PatternRewriter &rewriter,
                                       FIRToMemRefTypeConverter &typeConverter,
                                       Operation *memOp) {
+  firMemref = peelZeroOffsetViews(firMemref);
+
   Operation *memrefOp = firMemref.getDefiningOp();
   if (!memrefOp) {
     if (auto blockArg = dyn_cast<BlockArgument>(firMemref)) {
@@ -1504,20 +1526,6 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
     return std::pair{*converted, indices};
   }
 
-  if (auto declareOp = dyn_cast<fir::DeclareOp>(memrefOp)) {
-    FailureOr<Value> converted =
-        getFIRConvert(memOp, declareOp, rewriter, typeConverter);
-    if (failed(converted)) {
-      LLVM_DEBUG(llvm::dbgs()
-                     << "FIRToMemRef: unable to create convert for scalar "
-                        "memref:\n";
-                 firMemref.dump());
-      return failure();
-    }
-    SmallVector<Value> indices;
-    return std::pair{*converted, indices};
-  }
-
   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)`).
@@ -1558,39 +1566,6 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
     return std::pair{*converted, indices};
   }
 
-  if (auto convertOp = dyn_cast<fir::ConvertOp>(memrefOp)) {
-    Type fromTy = convertOp->getOperand(0).getType();
-    Type toTy = firMemref.getType();
-    if (isa<fir::ReferenceType>(fromTy) && isa<fir::ReferenceType>(toTy)) {
-      FailureOr<Value> converted =
-          getFIRConvert(memOp, convertOp, rewriter, typeConverter);
-      if (failed(converted)) {
-        LLVM_DEBUG(
-            llvm::dbgs()
-                << "FIRToMemRef: unable to create convert for conversion "
-                   "op:\n";
-            firMemref.dump());
-        return failure();
-      }
-      SmallVector<Value> indices;
-      return std::pair{*converted, indices};
-    }
-  }
-
-  if (auto boxAddrOp = dyn_cast<fir::BoxAddrOp>(memrefOp)) {
-    FailureOr<Value> converted =
-        getFIRConvert(memOp, boxAddrOp, rewriter, typeConverter);
-    if (failed(converted)) {
-      LLVM_DEBUG(llvm::dbgs()
-                     << "FIRToMemRef: unable to create convert for box_addr "
-                        "op:\n";
-                 firMemref.dump());
-      return failure();
-    }
-    SmallVector<Value> indices;
-    return std::pair{*converted, indices};
-  }
-
   if (memrefIsDeviceData(memrefOp)) {
     FailureOr<Value> converted =
         getFIRConvert(memOp, memrefOp, rewriter, typeConverter);
@@ -1608,9 +1583,15 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
 }
 
 void FIRToMemRef::replaceFIRMemrefs(Value firMemref, Value converted,
+                                    ArrayRef<Value> indices,
                                     PatternRewriter &rewriter) const {
+  // converted is only a base memref paired with indices, not a standalone
+  // address-equivalent for firMemref, so don't redirect other users to it.
+  if (!indices.empty())
+    return;
+
   Operation *op = firMemref.getDefiningOp();
-  if (op && (isa<fir::ArrayCoorOp>(op) || isMarshalLike(op)))
+  if (op && isMarshalLike(op))
     return;
 
   SmallPtrSet<Operation *, 4> worklist;
@@ -1721,7 +1702,7 @@ void FIRToMemRef::rewriteLoadOp(fir::LoadOp load, PatternRewriter &rewriter,
   }
 
   if (!isa<fir::LogicalType>(originalType))
-    replaceFIRMemrefs(firMemref, converted, rewriter);
+    replaceFIRMemrefs(firMemref, converted, indices, rewriter);
 }
 
 void FIRToMemRef::rewriteStoreOp(fir::StoreOp store, PatternRewriter &rewriter,
@@ -1775,7 +1756,7 @@ void FIRToMemRef::rewriteStoreOp(fir::StoreOp store, PatternRewriter &rewriter,
           llvm::dyn_cast<fir::ReferenceType>(firMemref.getType()))
     isLogicalRef = llvm::isa<fir::LogicalType>(refTy.getEleTy());
   if (!isLogicalRef)
-    replaceFIRMemrefs(firMemref, converted, rewriter);
+    replaceFIRMemrefs(firMemref, converted, indices, rewriter);
 }
 
 // Lower operand and result type of FIR logical operation to get rid

>From 77c0151179dc1999872e3bc2c41a128f2f14c92c Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Mon, 10 Aug 2026 14:10:08 -0700
Subject: [PATCH 2/4] tweak

---
 .../lib/Optimizer/Transforms/FIRToMemRef.cpp  | 50 ++++++++++++++++++-
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 7392516a9f377..e31ee5decbda9 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -1473,8 +1473,6 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
                                       PatternRewriter &rewriter,
                                       FIRToMemRefTypeConverter &typeConverter,
                                       Operation *memOp) {
-  firMemref = peelZeroOffsetViews(firMemref);
-
   Operation *memrefOp = firMemref.getDefiningOp();
   if (!memrefOp) {
     if (auto blockArg = dyn_cast<BlockArgument>(firMemref)) {
@@ -1575,6 +1573,54 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
     return std::pair{*converted, indices};
   }
 
+  // Some other zero-offset view (fir.declare, ref/box fir.convert,
+  // fir.box_addr, fir.volatile_cast, fir.create_box, unsliced
+  // fir.embox/fir.rebox): peek underneath for an fir.array_coor or indexing
+  // fir.coordinate_of that needs bounds-aware handling (e.g. fir.declare
+  // wrapping fir.array_coor); otherwise marshal the view itself so its
+  // naming is kept and getFIRConvert's dedup logic still applies.
+  if (isa<fir::FortranObjectViewOpInterface>(memrefOp)) {
+    if (Operation *peeledOp = peelZeroOffsetViews(firMemref).getDefiningOp()) {
+      if (auto arrayCoorOp = dyn_cast<fir::ArrayCoorOp>(peeledOp)) {
+        MemRefInfo memrefInfo =
+            convertArrayCoorOp(memOp, arrayCoorOp, rewriter, typeConverter);
+        if (succeeded(memrefInfo)) {
+          for (auto user : peeledOp->getUsers())
+            if (!isa<fir::LoadOp, fir::StoreOp>(user))
+              return memrefInfo;
+          eraseOps.insert(peeledOp);
+        }
+        return memrefInfo;
+      }
+
+      if (auto coordinateOp = dyn_cast<fir::CoordinateOp>(peeledOp);
+          coordinateOp &&
+          isArrayIndexingCoordinateOp(coordinateOp, typeConverter)) {
+        MemRefInfo memrefInfo = convertCoordinateArrayOp(
+            memOp, coordinateOp, rewriter, typeConverter);
+        if (succeeded(memrefInfo)) {
+          for (auto user : peeledOp->getUsers())
+            if (!isa<fir::LoadOp, fir::StoreOp>(user))
+              return memrefInfo;
+          eraseOps.insert(peeledOp);
+          return memrefInfo;
+        }
+      }
+    }
+
+    FailureOr<Value> converted =
+        getFIRConvert(memOp, memrefOp, rewriter, typeConverter);
+    if (failed(converted)) {
+      LLVM_DEBUG(llvm::dbgs()
+                     << "FIRToMemRef: unable to create convert for view "
+                        "memref:\n";
+                 firMemref.dump());
+      return failure();
+    }
+    SmallVector<Value> indices;
+    return std::pair{*converted, indices};
+  }
+
   LLVM_DEBUG(llvm::dbgs()
                  << "FIRToMemRef: unable to create convert for memref value:\n";
              firMemref.dump());

>From 0a33cf9aedf7ea159ccc67fb4961c09e5f6d52c9 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Mon, 10 Aug 2026 14:34:13 -0700
Subject: [PATCH 3/4] tweak

---
 .../lib/Optimizer/Transforms/FIRToMemRef.cpp  | 42 ++++++-------------
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index e31ee5decbda9..57c2359a7bce2 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -1573,39 +1573,23 @@ MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref,
     return std::pair{*converted, indices};
   }
 
-  // Some other zero-offset view (fir.declare, ref/box fir.convert,
-  // fir.box_addr, fir.volatile_cast, fir.create_box, unsliced
-  // fir.embox/fir.rebox): peek underneath for an fir.array_coor or indexing
-  // fir.coordinate_of that needs bounds-aware handling (e.g. fir.declare
+  // Some other zero-offset, ref-producing view (fir.declare, ref-to-ref
+  // fir.convert, fir.box_addr, fir.volatile_cast): peek underneath for an
+  // fir.array_coor that needs bounds-aware handling (e.g. fir.declare
   // wrapping fir.array_coor); otherwise marshal the view itself so its
   // naming is kept and getFIRConvert's dedup logic still applies.
   if (isa<fir::FortranObjectViewOpInterface>(memrefOp)) {
-    if (Operation *peeledOp = peelZeroOffsetViews(firMemref).getDefiningOp()) {
-      if (auto arrayCoorOp = dyn_cast<fir::ArrayCoorOp>(peeledOp)) {
-        MemRefInfo memrefInfo =
-            convertArrayCoorOp(memOp, arrayCoorOp, rewriter, typeConverter);
-        if (succeeded(memrefInfo)) {
-          for (auto user : peeledOp->getUsers())
-            if (!isa<fir::LoadOp, fir::StoreOp>(user))
-              return memrefInfo;
-          eraseOps.insert(peeledOp);
-        }
-        return memrefInfo;
-      }
-
-      if (auto coordinateOp = dyn_cast<fir::CoordinateOp>(peeledOp);
-          coordinateOp &&
-          isArrayIndexingCoordinateOp(coordinateOp, typeConverter)) {
-        MemRefInfo memrefInfo = convertCoordinateArrayOp(
-            memOp, coordinateOp, rewriter, typeConverter);
-        if (succeeded(memrefInfo)) {
-          for (auto user : peeledOp->getUsers())
-            if (!isa<fir::LoadOp, fir::StoreOp>(user))
-              return memrefInfo;
-          eraseOps.insert(peeledOp);
-          return memrefInfo;
-        }
+    if (auto arrayCoorOp = dyn_cast_or_null<fir::ArrayCoorOp>(
+            peelZeroOffsetViews(firMemref).getDefiningOp())) {
+      MemRefInfo memrefInfo =
+          convertArrayCoorOp(memOp, arrayCoorOp, rewriter, typeConverter);
+      if (succeeded(memrefInfo)) {
+        for (auto user : arrayCoorOp->getUsers())
+          if (!isa<fir::LoadOp, fir::StoreOp>(user))
+            return memrefInfo;
+        eraseOps.insert(arrayCoorOp);
       }
+      return memrefInfo;
     }
 
     FailureOr<Value> converted =

>From 26ab572422107f710220c3ed442914536b6e10b9 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Mon, 10 Aug 2026 15:03:09 -0700
Subject: [PATCH 4/4] add tests

---
 .../lib/Optimizer/Transforms/FIRToMemRef.cpp  |  15 +-
 .../FIRToMemRef/view-op-wraps-array-coor.mlir | 174 ++++++++++++++++++
 2 files changed, 183 insertions(+), 6 deletions(-)
 create mode 100644 flang/test/Transforms/FIRToMemRef/view-op-wraps-array-coor.mlir

diff --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 57c2359a7bce2..12443742da2b9 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -102,15 +102,18 @@ static bool isMarshalLike(Operation *op) {
 }
 
 /// Peel `FortranObjectViewOpInterface` ops with a statically-known zero
-/// offset (fir.declare, ref/box fir.convert, fir.box_addr,
-/// fir.volatile_cast, fir.create_box, unsliced fir.embox/fir.rebox) down to
-/// whatever produced the address underneath, e.g. fir.array_coor or a
-/// *sliced* embox/rebox (offset == nullopt, handled separately). Excludes a
-/// marshal-like fir.convert, which must go through its own dedicated path.
+/// offset (fir.declare, ref/box fir.convert, fir.box_addr, fir.create_box,
+/// unsliced fir.embox/fir.rebox) down to whatever produced the address
+/// underneath, e.g. fir.array_coor or a *sliced* embox/rebox (offset ==
+/// nullopt, handled separately). Excludes a marshal-like fir.convert, which
+/// must go through its own dedicated path. Also stops at fir.volatile_cast:
+/// since it is the only op that may change volatility, whatever it wraps may
+/// have different volatility than its result, so peeling past it could
+/// marshal a still-volatile value without going through the explicit cast.
 static Value peelZeroOffsetViews(Value memref) {
   while (Operation *defOp = memref.getDefiningOp()) {
     auto view = dyn_cast<fir::FortranObjectViewOpInterface>(defOp);
-    if (!view || isMarshalLike(defOp))
+    if (!view || isMarshalLike(defOp) || isa<fir::VolatileCastOp>(defOp))
       break;
     auto result = cast<OpResult>(memref);
     if (view.getViewOffset(result) != 0)
diff --git a/flang/test/Transforms/FIRToMemRef/view-op-wraps-array-coor.mlir b/flang/test/Transforms/FIRToMemRef/view-op-wraps-array-coor.mlir
new file mode 100644
index 0000000000000..fb0afea5c72e4
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/view-op-wraps-array-coor.mlir
@@ -0,0 +1,174 @@
+// Test that each zero-offset FortranObjectViewOpInterface op wrapping (directly
+// or through a chain of such views) a fir.array_coor is peeled through, so the
+// array_coor is still lowered to a bounds-aware memref access instead of being
+// treated as an opaque scalar reference.
+// RUN: fir-opt %s --fir-to-memref --allow-unregistered-dialect | FileCheck %s
+// The pass must not introduce any fir.convert that drops volatility.
+// RUN: fir-opt %s --strict-fir-volatile-verifier --fir-to-memref -o /dev/null
+
+// CHECK-LABEL: func.func @declare_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @declare_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %decl = fir.declare %elem {uniq_name = "x"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  %load = fir.load %decl : !fir.ref<i32>
+  fir.store %v to %decl : !fir.ref<i32>
+  return
+}
+
+// CHECK-LABEL: func.func @convert_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @convert_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %cvt = fir.convert %elem : (!fir.ref<i32>) -> !fir.ref<i32>
+  %load = fir.load %cvt : !fir.ref<i32>
+  fir.store %v to %cvt : !fir.ref<i32>
+  return
+}
+
+// A fir.box_addr can only reach an array_coor through an intervening
+// fir.embox/rebox/create_box (its own operand is always a box, and
+// array_coor never produces one), so this also exercises peeling through
+// fir.embox one level down.
+// CHECK-LABEL: func.func @box_addr_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @box_addr_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %box = fir.embox %elem : (!fir.ref<i32>) -> !fir.box<i32>
+  %addr = fir.box_addr %box : (!fir.box<i32>) -> !fir.ref<i32>
+  %load = fir.load %addr : !fir.ref<i32>
+  fir.store %v to %addr : !fir.ref<i32>
+  return
+}
+
+// Longer chain (fir.declare -> fir.box_addr -> fir.embox -> fir.array_coor)
+// to check multi-hop peeling through fir.embox specifically.
+// CHECK-LABEL: func.func @embox_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @embox_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %box = fir.embox %elem : (!fir.ref<i32>) -> !fir.box<i32>
+  %addr = fir.box_addr %box : (!fir.box<i32>) -> !fir.ref<i32>
+  %decl = fir.declare %addr {uniq_name = "x"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  %load = fir.load %decl : !fir.ref<i32>
+  fir.store %v to %decl : !fir.ref<i32>
+  return
+}
+
+// A fir.volatile_cast can go from volatile to non-volatile, e.g. after taking
+// the address of an element of a volatile array. Unlike the other view ops,
+// peeling must *not* look past it: the fir.array_coor underneath is still
+// volatile, so converting its base directly to memref would bypass the cast
+// and silently drop volatility. Instead only the (already non-volatile)
+// fir.volatile_cast result itself is marshaled, as a rank-0 memref, leaving
+// fir.array_coor untouched in FIR.
+// CHECK-LABEL: func.func @volatile_cast_wraps_array_coor
+// CHECK:       %[[COOR:.+]] = fir.array_coor %arg0{{.*}} : {{.*}} -> !fir.ref<i32, volatile>
+// CHECK:       %[[VC:.+]] = fir.volatile_cast %[[COOR]] : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+// CHECK:       %[[M0:.+]] = fir.convert %[[VC]] : (!fir.ref<i32>) -> memref<i32>
+// CHECK:       memref.load %[[M0]][]
+// CHECK:       %[[M1:.+]] = fir.convert %[[VC]] : (!fir.ref<i32>) -> memref<i32>
+// CHECK:       memref.store %arg1, %[[M1]][]
+func.func @volatile_cast_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>, volatile>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>, volatile>, !fir.shape<1>, index) -> !fir.ref<i32, volatile>
+  %vc = fir.volatile_cast %elem : (!fir.ref<i32, volatile>) -> !fir.ref<i32>
+  %load = fir.load %vc : !fir.ref<i32>
+  fir.store %v to %vc : !fir.ref<i32>
+  return
+}
+
+// fir.create_box wraps a non-contiguous byte-strided view (e.g. a
+// derived-type component array); box_addr unwraps it back to a ref.
+// CHECK-LABEL: func.func @create_box_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @create_box_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %c4 = arith.constant 4 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %arr1 = fir.convert %elem : (!fir.ref<i32>) -> !fir.ref<!fir.array<1xi32>>
+  %box = fir.create_box %arr1 lbs(%c1) extents(%c1) strides(%c4) : (!fir.ref<!fir.array<1xi32>>, index, index, index) -> !fir.box<!fir.array<1xi32>>
+  %addrarr = fir.box_addr %box : (!fir.box<!fir.array<1xi32>>) -> !fir.ref<!fir.array<1xi32>>
+  %addr = fir.convert %addrarr : (!fir.ref<!fir.array<1xi32>>) -> !fir.ref<i32>
+  %load = fir.load %addr : !fir.ref<i32>
+  fir.store %v to %addr : !fir.ref<i32>
+  return
+}
+
+// fir.rebox re-views an existing box (e.g. to change its shape); the box it
+// re-views can itself be an fir.embox wrapping the array_coor result.
+// CHECK-LABEL: func.func @rebox_wraps_array_coor
+// CHECK:       %[[M0:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       %[[M1:.+]] = fir.convert %arg0 : (!fir.ref<!fir.array<10xi32>>) -> memref<10xi32>
+// CHECK:       memref.load %[[M0]]
+// CHECK:       memref.store %arg1, %[[M1]]
+// CHECK-NOT:   fir.array_coor
+func.func @rebox_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %box = fir.embox %elem : (!fir.ref<i32>) -> !fir.box<i32>
+  %rebox = fir.rebox %box : (!fir.box<i32>) -> !fir.box<i32>
+  %addr = fir.box_addr %rebox : (!fir.box<i32>) -> !fir.ref<i32>
+  %load = fir.load %addr : !fir.ref<i32>
+  fir.store %v to %addr : !fir.ref<i32>
+  return
+}
+
+// Negative case: fir.volatile_cast going the other way, non-volatile to
+// volatile (the common direction, e.g. declaring a variable volatile). Here
+// the load/store's own type ends up volatile, so the pass must bail out
+// before ever reaching getMemRefInfo/peeling — nothing should be converted.
+// CHECK-LABEL: func.func @volatile_cast_to_volatile_wraps_array_coor
+// CHECK:       %[[COOR:.+]] = fir.array_coor %arg0{{.*}} : {{.*}} -> !fir.ref<i32>
+// CHECK:       %[[VC:.+]] = fir.volatile_cast %[[COOR]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+// CHECK:       fir.load %[[VC]] : !fir.ref<i32, volatile>
+// CHECK:       fir.store %arg1 to %[[VC]] : !fir.ref<i32, volatile>
+// CHECK-NOT:   memref
+func.func @volatile_cast_to_volatile_wraps_array_coor(%arg0: !fir.ref<!fir.array<10xi32>>, %v: i32) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+  %elem = fir.array_coor %arg0(%shape) %c1 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, index) -> !fir.ref<i32>
+  %vc = fir.volatile_cast %elem : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+  %load = fir.load %vc : !fir.ref<i32, volatile>
+  fir.store %v to %vc : !fir.ref<i32, volatile>
+  return
+}



More information about the flang-commits mailing list