[Mlir-commits] [flang] [mlir] [mlir][LLVMIR] Set memory effects for llvm.intrin.stacksave/restore. (PR #191918)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Apr 13 18:02:28 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-llvm

Author: Slava Zakharin (vzakhari)

<details>
<summary>Changes</summary>

These operations are too conservative currently.
Adding effects on `AutomaticAllocationScopeResource` improves
Flang LICM.


---
Full diff: https://github.com/llvm/llvm-project/pull/191918.diff


3 Files Affected:

- (added) flang/test/Transforms/licm-stacksaverestore.mlir (+35) 
- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td (+27-2) 
- (added) mlir/test/Dialect/LLVMIR/side-effects.mlir (+13) 


``````````diff
diff --git a/flang/test/Transforms/licm-stacksaverestore.mlir b/flang/test/Transforms/licm-stacksaverestore.mlir
new file mode 100644
index 0000000000000..e408ee9b09085
--- /dev/null
+++ b/flang/test/Transforms/licm-stacksaverestore.mlir
@@ -0,0 +1,35 @@
+// RUN: fir-opt -flang-licm --split-input-file %s | FileCheck %s
+
+// Verify that an invariant load is hoisted out of the loop
+// when stacksave/stackrestore are present.
+//
+// Original code:
+// subroutine test(x,y)
+//   real :: x(10), y
+//   do i=1,10
+//      block
+//        x(i) = y
+//      end block
+//   end do
+// end subroutine test
+// CHECK-LABEL:   func.func @_QPtest(
+// CHECK:           %[[DECLARE_1:.*]] = fir.declare %{{.*}} dummy_scope %{{.*}} arg 2 {uniq_name = "_QFtestEy"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+// CHECK:           %[[LOAD_0:.*]] = fir.load %[[DECLARE_1]] : !fir.ref<f32>
+// CHECK:           fir.do_loop
+// CHECK-NOT: fir.load
+func.func @_QPtest(%arg0: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "x"}, %arg1: !fir.ref<f32> {fir.bindc_name = "y"}) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %0 = fir.dummy_scope : !fir.dscope
+  %3 = fir.shape %c10 : (index) -> !fir.shape<1>
+  %4 = fir.declare %arg0(%3) dummy_scope %0 arg 1 {uniq_name = "_QFtestEx"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, !fir.dscope) -> !fir.ref<!fir.array<10xf32>>
+  %5 = fir.declare %arg1 dummy_scope %0 arg 2 {uniq_name = "_QFtestEy"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  fir.do_loop %arg2 = %c1 to %c10 step %c1 {
+    %8 = llvm.intr.stacksave : !llvm.ptr
+    %9 = fir.load %5 : !fir.ref<f32>
+    %12 = fir.array_coor %4(%3) %arg2 : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+    fir.store %9 to %12 : !fir.ref<f32>
+    llvm.intr.stackrestore %8 : !llvm.ptr
+  }
+  return
+}
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index 688bc19cbf18a..7945cf9fae71f 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -893,11 +893,36 @@ def LLVM_EhTypeidForOp : LLVM_OneResultIntrOp<"eh.typeid.for", [], [0]> {
 // Stack save/restore intrinsics.
 //
 
-def LLVM_StackSaveOp : LLVM_OneResultIntrOp<"stacksave", [0]> {
+// Technically, llvm.intrin.stacksave does not free the stack,
+// but MemFree<AutomaticAllocationScopeResource> seems to be
+// an appropriate effect to guarantee that the allocations
+// between stacksave/stackrestore cannot be moved out of
+// the stacksave/stackrestore "scope".
+// For example,
+//   %ptr = llvm.intrin.stacksave : !llvm.ptr
+//   %alloca = llvm.alloca %zero x i32 : (i32) -> !llvm.ptr
+//   ... = llvm.load %alloca : !llvm.ptr -> i32
+//   llvm.store %x, %alloca : i32, !llvm.ptr
+//   llvm.intrin.stackrestore %ptr : !llvm.ptr
+//
+// The 'llvm.alloca' should not be movable neither before
+// llvm.intrin.stacksave nor after llvm.intrin.stackrestore.
+// Moreover, llvm.intrin.stackrestore cannot be moved
+// above the load the the store.
+//
+// The 'Free' affect on 'AutomaticAllocationScopeResource'
+// for both stacksave and stackrestore should guarantee that.
+def LLVM_StackSaveOp
+    : LLVM_OneResultIntrOp<
+          "stacksave", [0], [],
+          [MemoryEffects<[MemFree<AutomaticAllocationScopeResource>]>]> {
   let assemblyFormat = "attr-dict `:` qualified(type($res))";
 }
 
-def LLVM_StackRestoreOp : LLVM_ZeroResultIntrOp<"stackrestore", [0]> {
+def LLVM_StackRestoreOp
+    : LLVM_ZeroResultIntrOp<
+          "stackrestore", [0],
+          [MemoryEffects<[MemFree<AutomaticAllocationScopeResource>]>]> {
   let arguments = (ins LLVM_AnyPointer:$ptr);
   let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))";
 }
diff --git a/mlir/test/Dialect/LLVMIR/side-effects.mlir b/mlir/test/Dialect/LLVMIR/side-effects.mlir
new file mode 100644
index 0000000000000..b3e10a95d2868
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/side-effects.mlir
@@ -0,0 +1,13 @@
+// RUN: mlir-opt %s --test-side-effects --verify-diagnostics
+
+func.func @test_stacksave_stackrestore() {
+  // expected-remark @below {{found an instance of 'free' on resource 'AutomaticAllocationScope'}}
+  %ptr = llvm.intr.stacksave : !llvm.ptr
+  // expected-remark @below {{operation has no memory effects}}
+  %c1 = llvm.mlir.constant(1 : i32) : i32
+  // expected-remark @below {{found an instance of 'allocate' on op result 0, on resource 'AutomaticAllocationScope'}}
+  %alloca = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr
+  // expected-remark @below {{found an instance of 'free' on resource 'AutomaticAllocationScope'}}
+  llvm.intr.stackrestore %ptr : !llvm.ptr
+  llvm.return
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/191918


More information about the Mlir-commits mailing list