[flang-commits] [flang] 5e67107 - [flang] Do not convert load and store FIR ops to memref if volatile (#212884)

via flang-commits flang-commits at lists.llvm.org
Fri Jul 31 10:10:33 PDT 2026


Author: Valery Dmitriev
Date: 2026-07-31T10:10:28-07:00
New Revision: 5e67107334216e076073817f4f7b96b5ec57d1d0

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

LOG: [flang] Do not convert load and store FIR ops to memref if volatile (#212884)

Memref dialect has no way to express volatility currently. Let such FIR
operations to stay as FIR to be handled by CodeGen in path to LLVM.

Added: 
    flang/test/Transforms/FIRToMemRef/volatile-codegen.mlir
    flang/test/Transforms/FIRToMemRef/volatile.mlir

Modified: 
    flang/lib/Optimizer/Transforms/FIRToMemRef.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 8faafaa1ffeed..5851022b1bc47 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -32,6 +32,10 @@
 //    and emits `memref.reinterpret_cast` when dynamic layout is required
 //    (TODO: use memref.cast instead).
 //
+//  - Leaves volatile `fir.load` / `fir.store` alone. Memref dialect has no way
+//    to express volatility currently. These accesses are lowered straight
+//    to LLVM by the FIR code generation path.
+//
 //===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
@@ -1673,6 +1677,12 @@ void FIRToMemRef::replaceFIRMemrefs(Value firMemref, Value converted,
 
 void FIRToMemRef::rewriteLoadOp(fir::LoadOp load, PatternRewriter &rewriter,
                                 FIRToMemRefTypeConverter &typeConverter) {
+  if (fir::isa_volatile_type(load.getMemref().getType())) {
+    LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: keeping volatile load in FIR:\n";
+               load.dump());
+    return;
+  }
+
   Value firMemref = load.getMemref();
   if (!typeConverter.convertibleType(firMemref.getType()))
     return;
@@ -1716,6 +1726,13 @@ void FIRToMemRef::rewriteLoadOp(fir::LoadOp load, PatternRewriter &rewriter,
 
 void FIRToMemRef::rewriteStoreOp(fir::StoreOp store, PatternRewriter &rewriter,
                                  FIRToMemRefTypeConverter &typeConverter) {
+  if (fir::isa_volatile_type(store.getMemref().getType()) ||
+      fir::isa_volatile_type(store.getValue().getType())) {
+    LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: keeping volatile store in FIR:\n";
+               store.dump());
+    return;
+  }
+
   Value firMemref = store.getMemref();
 
   if (!typeConverter.convertibleType(firMemref.getType()))

diff  --git a/flang/test/Transforms/FIRToMemRef/volatile-codegen.mlir b/flang/test/Transforms/FIRToMemRef/volatile-codegen.mlir
new file mode 100644
index 0000000000000..442cf6f7472e7
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/volatile-codegen.mlir
@@ -0,0 +1,17 @@
+// RUN: fir-opt %s --strict-fir-volatile-verifier --fir-to-memref -cg-rewrite --fir-to-llvm-ir | FileCheck %s
+
+// Volatile accesses skipped by fir-to-memref still reach LLVM IR through the
+// FIR code generation path, which sets the volatile flag from the reference
+// type.
+
+// CHECK-LABEL: llvm.func @volatile_roundtrip
+// CHECK:         %[[LOAD:.*]] = llvm.load volatile %{{.*}} : !llvm.ptr -> f32
+// CHECK:         llvm.store volatile %[[LOAD]], %{{.*}} : f32, !llvm.ptr
+func.func @volatile_roundtrip(%arg0: !fir.ref<f32>) {
+  %0 = fir.undefined !fir.dscope
+  %1 = fir.volatile_cast %arg0 : (!fir.ref<f32>) -> !fir.ref<f32, volatile>
+  %2 = fir.declare %1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "x"} : (!fir.ref<f32, volatile>, !fir.dscope) -> !fir.ref<f32, volatile>
+  %3 = fir.load %2 : !fir.ref<f32, volatile>
+  fir.store %3 to %2 : !fir.ref<f32, volatile>
+  return
+}

diff  --git a/flang/test/Transforms/FIRToMemRef/volatile.mlir b/flang/test/Transforms/FIRToMemRef/volatile.mlir
new file mode 100644
index 0000000000000..184bddb42d547
--- /dev/null
+++ b/flang/test/Transforms/FIRToMemRef/volatile.mlir
@@ -0,0 +1,77 @@
+// RUN: fir-opt %s --fir-to-memref | 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
+
+// memref dialect currently has no way to express volatile loads and stores.
+// This test checks that FIR loads and stores from/to volatile references
+// stay as FIR.
+
+// CHECK-LABEL: func.func @volatile_scalar_dummy
+// CHECK:         %[[CAST:.*]] = fir.volatile_cast %arg0 : (!fir.ref<f128>) -> !fir.ref<f128, volatile>
+// CHECK:         %[[DECL:.*]] = fir.declare %[[CAST]]
+// CHECK:         %[[LOAD:.*]] = fir.load %[[DECL]] : !fir.ref<f128, volatile>
+// CHECK:         fir.store %[[LOAD]] to %[[DECL]] : !fir.ref<f128, volatile>
+// CHECK-NOT:     memref
+func.func @volatile_scalar_dummy(%arg0: !fir.ref<f128>) {
+  %0 = fir.undefined !fir.dscope
+  %1 = fir.volatile_cast %arg0 : (!fir.ref<f128>) -> !fir.ref<f128, volatile>
+  %2 = fir.declare %1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "x"} : (!fir.ref<f128, volatile>, !fir.dscope) -> !fir.ref<f128, volatile>
+  %3 = fir.load %2 : !fir.ref<f128, volatile>
+  fir.store %3 to %2 : !fir.ref<f128, volatile>
+  return
+}
+
+// CHECK-LABEL: func.func @volatile_local
+// CHECK:         %[[ALLOCA:.*]] = memref.alloca() {bindc_name = "i", uniq_name = "i"} : memref<i32>
+// CHECK:         %[[CONV:.*]] = fir.convert %[[ALLOCA]] : (memref<i32>) -> !fir.ref<i32>
+// CHECK:         %[[VCAST:.*]] = fir.volatile_cast %[[CONV]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+// CHECK:         %[[DECL:.*]] = fir.declare %[[VCAST]] {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "i"}
+// CHECK:         fir.store %{{.*}} to %[[DECL]] : !fir.ref<i32, volatile>
+// CHECK:         %{{.*}} = fir.load %[[DECL]] : !fir.ref<i32, volatile>
+// CHECK-NOT:     memref.load
+// CHECK-NOT:     memref.store
+func.func @volatile_local() {
+  %c1_i32 = arith.constant 1 : i32
+  %0 = fir.alloca i32 {bindc_name = "i", uniq_name = "i"}
+  %1 = fir.volatile_cast %0 : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+  %2 = fir.declare %1 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "i"} : (!fir.ref<i32, volatile>) -> !fir.ref<i32, volatile>
+  fir.store %c1_i32 to %2 : !fir.ref<i32, volatile>
+  %3 = fir.load %2 : !fir.ref<i32, volatile>
+  return
+}
+
+// CHECK-LABEL: func.func @volatile_array_element
+// CHECK:         %[[VCAST:.*]] = fir.volatile_cast %arg0 : (!fir.ref<!fir.array<3xf32>>) -> !fir.ref<!fir.array<3xf32>, volatile>
+// CHECK:         %[[DECL:.*]] = fir.declare %[[VCAST]](%{{.*}}) dummy_scope {{.*}} {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "a"} : (!fir.ref<!fir.array<3xf32>, volatile>, !fir.shape<1>, !fir.dscope) -> !fir.ref<!fir.array<3xf32>, volatile>
+// CHECK:         %[[COOR:.*]] = fir.array_coor %[[DECL]](%{{.*}})
+// CHECK:         %{{.*}} = fir.load %[[COOR]] : !fir.ref<f32, volatile>
+// CHECK-NOT:     memref
+func.func @volatile_array_element(%arg0: !fir.ref<!fir.array<3xf32>>) {
+  %c1 = arith.constant 1 : index
+  %c3 = arith.constant 3 : index
+  %0 = fir.undefined !fir.dscope
+  %shape = fir.shape %c3 : (index) -> !fir.shape<1>
+  %1 = fir.volatile_cast %arg0 : (!fir.ref<!fir.array<3xf32>>) -> !fir.ref<!fir.array<3xf32>, volatile>
+  %2 = fir.declare %1(%shape) dummy_scope %0 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "a"} : (!fir.ref<!fir.array<3xf32>, volatile>, !fir.shape<1>, !fir.dscope) -> !fir.ref<!fir.array<3xf32>, volatile>
+  %3 = fir.array_coor %2(%shape) %c1 : (!fir.ref<!fir.array<3xf32>, volatile>, !fir.shape<1>, index) -> !fir.ref<f32, volatile>
+  %4 = fir.load %3 : !fir.ref<f32, volatile>
+  return
+}
+
+// CHECK-LABEL: func.func @mixed_volatile_and_plain
+// CHECK:         %[[VCAST:.*]] = fir.volatile_cast %arg0 : (!fir.ref<f32>) -> !fir.ref<f32, volatile>
+// CHECK:         %[[VDECL:.*]] = fir.declare %[[VCAST]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "v"} : (!fir.ref<f32, volatile>, !fir.dscope) -> !fir.ref<f32, volatile>
+// CHECK:         %[[PDECL:.*]] = fir.declare %arg1 dummy_scope %{{.*}} {uniq_name = "p"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+// CHECK:         %[[VLOAD:.*]] = fir.load %[[VDECL]] : !fir.ref<f32, volatile>
+// CHECK:         %[[PCONV:.*]] = fir.convert %[[PDECL]] : (!fir.ref<f32>) -> memref<f32>
+// CHECK:         memref.store %[[VLOAD]], %[[PCONV]][] : memref<f32>
+// CHECK-NOT:     memref.load
+func.func @mixed_volatile_and_plain(%arg0: !fir.ref<f32>, %arg1: !fir.ref<f32>) {
+  %0 = fir.undefined !fir.dscope
+  %1 = fir.volatile_cast %arg0 : (!fir.ref<f32>) -> !fir.ref<f32, volatile>
+  %2 = fir.declare %1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "v"} : (!fir.ref<f32, volatile>, !fir.dscope) -> !fir.ref<f32, volatile>
+  %3 = fir.declare %arg1 dummy_scope %0 {uniq_name = "p"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %4 = fir.load %2 : !fir.ref<f32, volatile>
+  fir.store %4 to %3 : !fir.ref<f32>
+  return
+}


        


More information about the flang-commits mailing list