[flang-commits] [flang] ef4fb18 - [flang][hlfir] Resolve shape_of users when bufferizing eval_in_mem (#201214)

via flang-commits flang-commits at lists.llvm.org
Wed Jun 3 06:51:25 PDT 2026


Author: khaki3
Date: 2026-06-03T06:51:20-07:00
New Revision: ef4fb183a8d5c0d1910f7b5b6776ba669d6795ef

URL: https://github.com/llvm/llvm-project/commit/ef4fb183a8d5c0d1910f7b5b6776ba669d6795ef
DIFF: https://github.com/llvm/llvm-project/commit/ef4fb183a8d5c0d1910f7b5b6776ba669d6795ef.diff

LOG: [flang][hlfir] Resolve shape_of users when bufferizing eval_in_mem (#201214)

A follow-up to #197814.

Example:

```fortran
bmat = matmul(mat, mat)   ! bmat is allocatable
```

In this code, `SeparateAllocatableAssign` sizes the reallocation with an
`hlfir.shape_of` of the RHS. Once the `matmul` is lowered to
`hlfir.eval_in_mem`, that `shape_of` is an extra user, so
`EvaluateIntoMemoryAssignBufferization` erases the `eval_in_mem` while
it's still used, hitting a `use-after-erase` assertion at `-O2`.

Fix: in `OptimizedBufferization`, redirect a `shape_of` user to the
`eval_in_mem`'s shape operand before erasing it.

Added: 
    

Modified: 
    flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
    flang/test/HLFIR/opt-bufferization-eval_in_mem.fir

Removed: 
    


################################################################################
diff  --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
index 51af673406b4a..d717b39479380 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp
@@ -638,16 +638,30 @@ tryUsingAssignLhsDirectly(hlfir::EvaluateInMemoryOp evalInMem,
   mlir::Location loc = evalInMem.getLoc();
   hlfir::DestroyOp destroy;
   hlfir::AssignOp assign;
-  for (auto user : llvm::enumerate(evalInMem->getUsers())) {
-    if (user.index() > 2)
+  // To evaluate the hlfir.eval_in_mem directly into the LHS, its result must
+  // only be used in the assignment, in a destroy, and in hlfir.shape_of (which
+  // can be replaced by a direct use of the shape operand).
+  llvm::SmallVector<hlfir::ShapeOfOp> shapeOfs;
+  for (mlir::Operation *user : evalInMem->getUsers()) {
+    if (auto op = mlir::dyn_cast<hlfir::AssignOp>(user)) {
+      if (assign)
+        return mlir::failure();
+      assign = op;
+    } else if (auto op = mlir::dyn_cast<hlfir::DestroyOp>(user)) {
+      if (destroy)
+        return mlir::failure();
+      destroy = op;
+    } else if (auto op = mlir::dyn_cast<hlfir::ShapeOfOp>(user)) {
+      shapeOfs.push_back(op);
+    } else {
       return mlir::failure();
-    mlir::TypeSwitch<mlir::Operation *, void>(user.value())
-        .Case([&](hlfir::AssignOp op) { assign = op; })
-        .Case([&](hlfir::DestroyOp op) { destroy = op; });
+    }
   }
   if (!assign || !destroy || destroy.mustFinalizeExpr() ||
       assign.isAllocatableAssignment())
     return mlir::failure();
+  if (!shapeOfs.empty() && !evalInMem.getShape())
+    return mlir::failure();
 
   hlfir::Entity lhs{assign.getLhs()};
   // EvaluateInMemoryOp memory is contiguous, so in general, it can only be
@@ -690,6 +704,10 @@ tryUsingAssignLhsDirectly(hlfir::EvaluateInMemoryOp evalInMem,
   fir::FirOpBuilder builder(rewriter, evalInMem.getOperation());
   mlir::Value rawLhs = hlfir::genVariableRawAddress(loc, builder, lhs);
   hlfir::computeEvaluateOpIn(loc, builder, evalInMem, rawLhs);
+  // Redirect shape_of users to the shape operand so the eval_in_mem can be
+  // erased without leaving dangling uses.
+  for (hlfir::ShapeOfOp shapeOf : shapeOfs)
+    rewriter.replaceOp(shapeOf, evalInMem.getShape());
   rewriter.eraseOp(assign);
   rewriter.eraseOp(destroy);
   rewriter.eraseOp(evalInMem);

diff  --git a/flang/test/HLFIR/opt-bufferization-eval_in_mem.fir b/flang/test/HLFIR/opt-bufferization-eval_in_mem.fir
index fdfb2ce6979dc..9ef8115284514 100644
--- a/flang/test/HLFIR/opt-bufferization-eval_in_mem.fir
+++ b/flang/test/HLFIR/opt-bufferization-eval_in_mem.fir
@@ -60,3 +60,33 @@ func.func @_QPnegative_test_is_target(%arg0: !fir.ref<!fir.array<10xf32>> {fir.b
 // CHECK:         hlfir.destroy %[[VAL_10]] : !hlfir.expr<10xf32>
 // CHECK:         return
 // CHECK:       }
+
+// A hlfir.shape_of user of the eval_in_mem (such as the one left behind by
+// SeparateAllocatableAssign when sizing a reallocation) must not block the
+// in-place rewrite: it is redirected to the eval_in_mem shape operand.
+func.func @_QPtest_shape_of_user(%arg0: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "x"}, %arg1: !fir.ref<index>) {
+  %c10 = arith.constant 10 : index
+  %0 = fir.dummy_scope : !fir.dscope
+  %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+  %2:2 = hlfir.declare %arg0(%1) dummy_scope %0 {uniq_name = "_QFtestEx"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+  %3 = hlfir.eval_in_mem shape %1 : (!fir.shape<1>) -> !hlfir.expr<10xf32> {
+  ^bb0(%arg2: !fir.ref<!fir.array<10xf32>>):
+    %4 = fir.call @_QPfoo() fastmath<contract> : () -> !fir.array<10xf32>
+    fir.save_result %4 to %arg2(%1) : !fir.array<10xf32>, !fir.ref<!fir.array<10xf32>>, !fir.shape<1>
+  }
+  %shp = hlfir.shape_of %3 : (!hlfir.expr<10xf32>) -> !fir.shape<1>
+  %ext = hlfir.get_extent %shp {dim = 0 : index} : (!fir.shape<1>) -> index
+  hlfir.assign %3 to %2#0 : !hlfir.expr<10xf32>, !fir.ref<!fir.array<10xf32>>
+  hlfir.destroy %3 : !hlfir.expr<10xf32>
+  fir.store %ext to %arg1 : !fir.ref<index>
+  return
+}
+// CHECK-LABEL: func.func @_QPtest_shape_of_user(
+// CHECK:         %[[SHAPE:.*]] = fir.shape %{{.*}} : (index) -> !fir.shape<1>
+// The shape_of is redirected to the eval_in_mem shape operand...
+// CHECK:         hlfir.get_extent %[[SHAPE]]
+// ...and the result is still evaluated directly into the LHS (no temporary).
+// CHECK:         %[[CALL:.*]] = fir.call @_QPfoo() fastmath<contract> : () -> !fir.array<10xf32>
+// CHECK:         fir.save_result %[[CALL]] to %{{.*}}#0(%[[SHAPE]])
+// CHECK-NOT:     hlfir.eval_in_mem
+// CHECK-NOT:     hlfir.shape_of


        


More information about the flang-commits mailing list