[Mlir-commits] [mlir] [MLIR][EmitC] Mark CastOp as Pure (PR #202749)

Andrzej WarzyƄski llvmlistbot at llvm.org
Fri Jun 12 07:49:57 PDT 2026


https://github.com/banach-space updated https://github.com/llvm/llvm-project/pull/202749

>From 36c29b3fe2bfe6581cb4d0a3064056b9fcc89d81 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Tue, 9 Jun 2026 19:39:51 +0000
Subject: [PATCH 1/3] [MLIR][EmitC] Mark CastOp as Pure

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 2 +-
 mlir/test/Dialect/EmitC/canonicalize.mlir   | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)
 create mode 100644 mlir/test/Dialect/EmitC/canonicalize.mlir

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 65361a987a08e..09fad02da39b2 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -394,7 +394,7 @@ def EmitC_MemberCallOpaqueOp : EmitC_Op<"member_call_opaque", [CExpressionInterf
 }
 
 def EmitC_CastOp : EmitC_Op<"cast",
-    [CExpressionInterface,
+    [Pure, CExpressionInterface,
      DeclareOpInterfaceMethods<CastOpInterface>]> {
   let summary = "Cast operation";
   let description = [{
diff --git a/mlir/test/Dialect/EmitC/canonicalize.mlir b/mlir/test/Dialect/EmitC/canonicalize.mlir
new file mode 100644
index 0000000000000..a2c41375da398
--- /dev/null
+++ b/mlir/test/Dialect/EmitC/canonicalize.mlir
@@ -0,0 +1,9 @@
+// RUN: mlir-opt %s -canonicalize="test-convergence" -split-input-file | FileCheck %s
+
+// While there is no dedicated folder for CastOp, it is Pure and hence should
+// be "folded away".
+func.func @cast(%arg0: i32) {
+  // CHECK-NOT: emitc.cast
+  %1 = emitc.cast %arg0: i32 to f32
+  return
+}

>From 055d86ac70f53316bae75f4874a1d2cbcac64f54 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Wed, 10 Jun 2026 16:47:36 +0000
Subject: [PATCH 2/3] Swich from unconditional PURE to opt-in

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 14 +++++++++++---
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         | 15 +++++++++++++++
 mlir/test/Dialect/EmitC/canonicalize.mlir   | 12 ++++++++----
 3 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 09fad02da39b2..4527afeedc346 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -394,8 +394,11 @@ def EmitC_MemberCallOpaqueOp : EmitC_Op<"member_call_opaque", [CExpressionInterf
 }
 
 def EmitC_CastOp : EmitC_Op<"cast",
-    [Pure, CExpressionInterface,
-     DeclareOpInterfaceMethods<CastOpInterface>]> {
+    [CExpressionInterface,
+     DeclareOpInterfaceMethods<CastOpInterface>,
+     DeclareOpInterfaceMethods<ConditionallySpeculatable>,
+     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
+    ]> {
   let summary = "Cast operation";
   let description = [{
     The `emitc.cast` operation performs an explicit type conversion and is emitted
@@ -414,7 +417,10 @@ def EmitC_CastOp : EmitC_Op<"cast",
     ```
   }];
 
-  let arguments = (ins EmitCType:$source);
+  let arguments = (ins EmitCType:$source,
+    UnitAttr:$pure
+  );
+
   let results = (outs EmitCType:$dest);
   let assemblyFormat = "$source attr-dict `:` type($source) `to` type($dest)";
 
@@ -422,6 +428,8 @@ def EmitC_CastOp : EmitC_Op<"cast",
     bool hasSideEffects() {
       return false;
     }
+
+    bool isPure();
   }];
 }
 
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index bab9cb4a91102..288ab0fe5215f 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -336,6 +336,21 @@ bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
        emitc::isSupportedFloatType(output) || isa<emitc::PointerType>(output)));
 }
 
+bool CastOp::isPure() { return static_cast<bool>(getPureAttr()); }
+
+Speculation::Speculatability emitc::CastOp::getSpeculatability() {
+  return getPure() ? Speculation::Speculatable : Speculation::NotSpeculatable;
+}
+
+void emitc::CastOp::getEffects(
+    SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
+  if (getPure())
+    return;
+
+  effects.emplace_back(MemoryEffects::Read::get());
+  effects.emplace_back(MemoryEffects::Write::get());
+}
+
 //===----------------------------------------------------------------------===//
 // CallOpaqueOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/EmitC/canonicalize.mlir b/mlir/test/Dialect/EmitC/canonicalize.mlir
index a2c41375da398..29d2fd157229d 100644
--- a/mlir/test/Dialect/EmitC/canonicalize.mlir
+++ b/mlir/test/Dialect/EmitC/canonicalize.mlir
@@ -1,9 +1,13 @@
 // RUN: mlir-opt %s -canonicalize="test-convergence" -split-input-file | FileCheck %s
 
-// While there is no dedicated folder for CastOp, it is Pure and hence should
-// be "folded away".
-func.func @cast(%arg0: i32) {
-  // CHECK-NOT: emitc.cast
+func.func @no_fold_cast_to_f32(%arg0: i32) {
+  // CHECK: emitc.cast
   %1 = emitc.cast %arg0: i32 to f32
   return
 }
+
+func.func @fold_cast_to_i64(%arg0: i32) {
+  // CHECK-NOT: emitc.cast
+  %1 = emitc.cast %arg0 {pure} : i32 to i64
+  return
+}

>From 373675cca6bcf33f55171280f6885b64d5c009dd Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Fri, 12 Jun 2026 14:49:43 +0000
Subject: [PATCH 3/3] Add comments

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 4527afeedc346..e30a5b07a1fe4 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -417,6 +417,14 @@ def EmitC_CastOp : EmitC_Op<"cast",
     ```
   }];
 
+  // In general, C++ cast expressions cannot always be assumed to be pure: they
+  // may invoke user-defined conversions or be affected by floating-point
+  // environment settings. However, in many practical cases, such as integer casts
+  // without operator overloading, the cast is pure and can be treated as
+  // speculatable and side-effect free.
+  //
+  // When this attribute is set, `getSpeculatability()` returns `Speculatable`
+  // and `getEffects()` reports no effects.
   let arguments = (ins EmitCType:$source,
     UnitAttr:$pure
   );



More information about the Mlir-commits mailing list