[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