[Mlir-commits] [mlir] [mlir] Make MemoryEffectsOpInterface optional. (PR #187427)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Mar 18 19:15:40 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Slava Zakharin (vzakhari)

<details>
<summary>Changes</summary>

For some instances of operations like calls it may be possible to report
specific memory effects and report "unknown" memory effects for other
instances. This patch makes MemoryEffectsOpInterface optional,
so that operations may implement it based on the particular instance.

RFC: https://discourse.llvm.org/t/rfc-mlir-memory-region-hierarchy-for-mlir-side-effects/89811/32

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


7 Files Affected:

- (modified) mlir/include/mlir-c/Interfaces.h (+3) 
- (modified) mlir/include/mlir/Interfaces/SideEffectInterfaces.td (+23) 
- (modified) mlir/lib/CAPI/Interfaces/Interfaces.cpp (+6) 
- (modified) mlir/test/Analysis/test-alias-analysis-modref.mlir (+34) 
- (modified) mlir/test/IR/test-side-effects.mlir (+15) 
- (modified) mlir/test/lib/Dialect/Test/TestOpDefs.cpp (+29) 
- (modified) mlir/test/lib/Dialect/Test/TestOps.td (+7) 


``````````diff
diff --git a/mlir/include/mlir-c/Interfaces.h b/mlir/include/mlir-c/Interfaces.h
index 17a812dcd86a9..5a4035f912319 100644
--- a/mlir/include/mlir-c/Interfaces.h
+++ b/mlir/include/mlir-c/Interfaces.h
@@ -113,6 +113,9 @@ typedef struct {
   /// Get memory effects callback.
   void (*getEffects)(MlirOperation op, MlirMemoryEffectInstancesList effects,
                      void *userData);
+  /// Callback that returns true iff the operation instance
+  /// can specify all its memory effects.
+  bool (*hasKnownMemoryEffects)(MlirOperation op);
   void *userData;
 } MlirMemoryEffectsOpInterfaceCallbacks;
 
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
index b292174fccb36..f8e1605556b97 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
@@ -31,6 +31,29 @@ def MemoryEffectsOpInterface
     an operation.
   }];
   let cppNamespace = "::mlir";
+
+  let append methods = [InterfaceMethod<
+                            [{
+      Returns true if the operation instance can specify all its memory effects.
+      The implementations may override this to make MemoryEffectsOpInterface
+      support conditional, e.g. when an operation may return conservative
+      "unknown" memory effects presenting itself as if it does not support
+      MemoryEffectsOpInterface at all.
+      By default, for all operations that are declared with
+      MemoryEffectsOpInterface support this method returns true.
+    }],
+                            /*retTy=*/"bool",
+                            /*methodName=*/"hasKnownMemoryEffects",
+                            /*args=*/(ins),
+                            /*methodBody=*/"",
+                            /*defaultImplementation=*/"return true;">,
+  ];
+
+  let extraClassOf = [{
+    // If hasKnownMemoryEffects() returns true, pretend that
+    // the operation instance does not implement MemoryEffectsOpInterface.
+    return $_op.hasKnownMemoryEffects();
+  }];
 }
 
 // The base class for defining specific memory effects.
diff --git a/mlir/lib/CAPI/Interfaces/Interfaces.cpp b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
index 437a1dbab9dae..9a3cd6932cf5e 100644
--- a/mlir/lib/CAPI/Interfaces/Interfaces.cpp
+++ b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
@@ -213,6 +213,12 @@ class MemoryEffectOpInterfaceFallbackModel
     callbacks.getEffects(wrap(op), cEffects, callbacks.userData);
   }
 
+  bool hasKnownMemoryEffects(Operation *op) const {
+    if (callbacks.hasKnownMemoryEffects)
+      return callbacks.hasKnownMemoryEffects(wrap(op));
+    return true;
+  }
+
 private:
   MlirMemoryEffectsOpInterfaceCallbacks callbacks;
 };
diff --git a/mlir/test/Analysis/test-alias-analysis-modref.mlir b/mlir/test/Analysis/test-alias-analysis-modref.mlir
index b82f789f4d77d..967974691dd58 100644
--- a/mlir/test/Analysis/test-alias-analysis-modref.mlir
+++ b/mlir/test/Analysis/test-alias-analysis-modref.mlir
@@ -89,3 +89,37 @@ func.func @addressable_write(%arg: memref<2xf32>) attributes {test.ptr = "func"}
   "test.side_effect_op"() {effects = [{effect="write"}], test.ptr = "side_effect_op"} : () -> i32
   return {test.ptr = "return"}
 }
+
+// -----
+
+// An op with known empty effects does not mod-ref any memory.
+// CHECK-LABEL: Testing : "optional_known_no_effects"
+// CHECK-DAG: optional_side_effect_op -> func.region0#0: NoModRef
+// CHECK-DAG: return -> func.region0#0: NoModRef
+func.func @optional_known_no_effects(%arg: memref<2xf32>) attributes {test.ptr = "func"} {
+  "test.optional_side_effect_op"() {effects = [], test.ptr = "optional_side_effect_op"} : () -> i32
+  return {test.ptr = "return"}
+}
+
+// -----
+
+// An op with unknown effects (no effects attr, hasKnownMemoryEffects = false)
+// is treated conservatively -> ModRef.
+// CHECK-LABEL: Testing : "optional_unknown_effects"
+// CHECK-DAG: optional_side_effect_op -> func.region0#0: ModRef
+// CHECK-DAG: return -> func.region0#0: NoModRef
+func.func @optional_unknown_effects(%arg: memref<2xf32>) attributes {test.ptr = "func"} {
+  "test.optional_side_effect_op"() {test.ptr = "optional_side_effect_op"} : () -> i32
+  return {test.ptr = "return"}
+}
+
+// -----
+
+// An op with a known read effect on an addressable resource -> Ref only.
+// CHECK-LABEL: Testing : "optional_known_read"
+// CHECK-DAG: optional_side_effect_op -> func.region0#0: Ref
+// CHECK-DAG: return -> func.region0#0: NoModRef
+func.func @optional_known_read(%arg: memref<2xf32>) attributes {test.ptr = "func"} {
+  "test.optional_side_effect_op"() {effects = [{effect="read"}], test.ptr = "optional_side_effect_op"} : () -> i32
+  return {test.ptr = "return"}
+}
diff --git a/mlir/test/IR/test-side-effects.mlir b/mlir/test/IR/test-side-effects.mlir
index b652ecb7dad1d..f04aeb9e6b696 100644
--- a/mlir/test/IR/test-side-effects.mlir
+++ b/mlir/test/IR/test-side-effects.mlir
@@ -56,5 +56,20 @@ func.func @side_effect(%arg : index) {
     test.region_yield %arg0 : index 
   } {effects = [ {effect="allocate", on_argument, test_resource} ]} : index -> index
 
+  // Known to have no memory effects (effects attr present but empty).
+  // expected-remark at +1 {{operation has no memory effects}}
+  %9 = "test.optional_side_effect_op"() {effects = []} : () -> i32
+
+  // Unknown effects (no effects attr) — the op does not cast to
+  // MemoryEffectOpInterface so the walk skips it; no diagnostic expected.
+  %10 = "test.optional_side_effect_op"() {} : () -> i32
+
+  // Known specific effects: read and write on <Default>.
+  // expected-remark at +2 {{found an instance of 'read' on resource '<Default>'}}
+  // expected-remark at +1 {{found an instance of 'write' on resource '<Default>'}}
+  %11 = "test.optional_side_effect_op"() {effects = [
+    {effect="read"}, {effect="write"}
+  ]} : () -> i32
+
   func.return 
 }
diff --git a/mlir/test/lib/Dialect/Test/TestOpDefs.cpp b/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
index ed5dc5bead78a..5a36379bca1e1 100644
--- a/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
+++ b/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
@@ -501,6 +501,35 @@ void SideEffectOp::getEffects(
   testSideEffectOpGetEffect(getOperation(), effects);
 }
 
+//===----------------------------------------------------------------------===//
+// OptionalSideEffectOp
+//===----------------------------------------------------------------------===//
+
+bool OptionalSideEffectOp::hasKnownMemoryEffects() {
+  return (*this)->hasAttr("effects");
+}
+
+void OptionalSideEffectOp::getEffects(
+    SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
+  ArrayAttr effectsAttr = (*this)->getAttrOfType<ArrayAttr>("effects");
+  if (!effectsAttr)
+    return;
+
+  for (Attribute element : effectsAttr) {
+    DictionaryAttr effectElement = cast<DictionaryAttr>(element);
+
+    MemoryEffects::Effect *effect =
+        StringSwitch<MemoryEffects::Effect *>(
+            cast<StringAttr>(effectElement.get("effect")).getValue())
+            .Case("allocate", MemoryEffects::Allocate::get())
+            .Case("free", MemoryEffects::Free::get())
+            .Case("read", MemoryEffects::Read::get())
+            .Case("write", MemoryEffects::Write::get());
+
+    effects.emplace_back(effect, SideEffects::DefaultResource::get());
+  }
+}
+
 void SideEffectWithRegionOp::getEffects(
     SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
   // Check for an effects attribute on the op instance.
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 4c9e6b3fe9e45..298d7dcaa23f4 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -2472,6 +2472,13 @@ def SideEffectOp : TEST_Op<"side_effect_op",
   let results = (outs AnyType:$result);
 }
 
+def OptionalSideEffectOp
+    : TEST_Op<"optional_side_effect_op",
+              [DeclareOpInterfaceMethods<
+                  MemoryEffectsOpInterface, ["hasKnownMemoryEffects"]>]> {
+  let results = (outs AnyType:$result);
+}
+
 def SideEffectWithRegionOp : TEST_Op<"side_effect_with_region_op",
     [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
      DeclareOpInterfaceMethods<TestEffectOpInterface>]> {

``````````

</details>


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


More information about the Mlir-commits mailing list