[Mlir-commits] [mlir] [MLIR][EmitC] Mark CastOp as Pure (PR #202749)
Andrzej WarzyĆski
llvmlistbot at llvm.org
Mon Jun 15 03:23:32 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/5] [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/5] 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 498106a26073f7c4b7d68b374fd1b04c09d619f6 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/5] 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..19971f2198193 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -418,6 +418,14 @@ def EmitC_CastOp : EmitC_Op<"cast",
}];
let arguments = (ins EmitCType:$source,
+ // 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.
UnitAttr:$pure
);
>From a108de1cfe61e27b90590d9b2a6bcebe11b7ea31 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Fri, 12 Jun 2026 15:07:05 +0000
Subject: [PATCH 4/5] Address comments from Jared
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 6 +++---
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 2 --
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 19971f2198193..f0606db7af7f5 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -434,10 +434,10 @@ def EmitC_CastOp : EmitC_Op<"cast",
let extraClassDeclaration = [{
bool hasSideEffects() {
- return false;
+ // Use the "pure" attribute to see whether this CastOp has side effects.
+ // Note that by default, `pure` is not set.
+ return getPure();
}
-
- bool isPure();
}];
}
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 288ab0fe5215f..c7bfb92fd6d40 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -336,8 +336,6 @@ 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;
}
>From 79b6b049a0318644499da7c77f310c06a77713cb Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Mon, 15 Jun 2026 10:23:15 +0000
Subject: [PATCH 5/5] Move the comment
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index f0606db7af7f5..32a07e701b208 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -415,17 +415,21 @@ def EmitC_CastOp : EmitC_Op<"cast",
%1 = emitc.cast %arg1 :
!emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
```
+
+ 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. For such cases, the `pure` attribute
+ may be used.
+
+ When `pure` attribute is set, `getSpeculatability()` returns `Speculatable`
+ and `getEffects()` reports no effects. It is UB if the `pure` attribute is
+ set and the actual conversion is not pure, e.g. when the user-defined
+ conversion has memory effects.
}];
let arguments = (ins EmitCType:$source,
- // 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.
UnitAttr:$pure
);
More information about the Mlir-commits
mailing list