[Mlir-commits] [mlir] [MLIR][Arith] Fix BitcastOp fold crashing on unhandled constant attributes (PR #212483)

Berke Ates llvmlistbot at llvm.org
Mon Aug 3 01:02:17 PDT 2026


https://github.com/Berke-Ates updated https://github.com/llvm/llvm-project/pull/212483

>From c0aecbaefdcac336483f549e5d4551a873f5da80 Mon Sep 17 00:00:00 2001
From: Berke-Ates <berke at ates.ch>
Date: Tue, 28 Jul 2026 15:08:20 +0200
Subject: [PATCH 1/4] [MLIR][Arith] Fix BitcastOp fold crashing on unhandled
 constant attributes

BitcastOp::fold assumed any non-poison scalar operand attribute is a
FloatAttr or IntegerAttr and hard-casted it. Constant attributes from
other dialects, e.g. the LLVM::UndefAttr produced by llvm.mlir.undef's
fold, hit the cast assertion. This crashed SCCP on IR where
llvm.mlir.undef feeds arith.bitcast. Bail out on attributes and result
types the fold does not handle.
---
 mlir/lib/Dialect/Arith/IR/ArithOps.cpp | 15 +++++++++++----
 mlir/test/Transforms/sccp.mlir         | 16 ++++++++++++++++
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index b7fdb97aba335..14cc8718d51a5 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -2270,16 +2270,23 @@ OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
     return ub::PoisonAttr::get(getContext());
 
   /// Bitcast integer or float to integer or float.
-  APInt bits = llvm::isa<FloatAttr>(operand)
-                   ? llvm::cast<FloatAttr>(operand).getValue().bitcastToAPInt()
-                   : llvm::cast<IntegerAttr>(operand).getValue();
+  APInt bits;
+  if (auto floatAttr = dyn_cast<FloatAttr>(operand))
+    bits = floatAttr.getValue().bitcastToAPInt();
+  else if (auto intAttr = dyn_cast<IntegerAttr>(operand))
+    bits = intAttr.getValue();
+  else
+    return {};
+
   assert(resType.getIntOrFloatBitWidth() == bits.getBitWidth() &&
          "trying to fold on broken IR: operands have incompatible types");
 
   if (auto resFloatType = dyn_cast<FloatType>(resType))
     return FloatAttr::get(resType,
                           APFloat(resFloatType.getFloatSemantics(), bits));
-  return IntegerAttr::get(resType, bits);
+  if (auto resIntType = dyn_cast<IntegerType>(resType))
+    return IntegerAttr::get(resType, bits);
+  return {};
 }
 
 void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
diff --git a/mlir/test/Transforms/sccp.mlir b/mlir/test/Transforms/sccp.mlir
index 251a74dc20647..b7ce60320d465 100644
--- a/mlir/test/Transforms/sccp.mlir
+++ b/mlir/test/Transforms/sccp.mlir
@@ -335,3 +335,19 @@ func.func @fold_to_non_operand_value(%x: i64, %cond: i1) -> i64 {
   %cast2 = builtin.unrealized_conversion_cast %cast1 : index to i64
   return %cast2 : i64
 }
+
+// -----
+
+// SCCP propagates the constant attribute produced by llvm.mlir.undef's fold
+// (an LLVM::UndefAttr) into arith.bitcast's fold, which must gracefully bail
+// instead of asserting.
+
+// CHECK-LABEL: func @bitcast_of_foreign_constant_attr
+func.func @bitcast_of_foreign_constant_attr() -> f64 {
+  // CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : i64
+  // CHECK: %[[CAST:.*]] = arith.bitcast %[[UNDEF]] : i64 to f64
+  // CHECK: return %[[CAST]] : f64
+  %0 = llvm.mlir.undef : i64
+  %1 = arith.bitcast %0 : i64 to f64
+  return %1 : f64
+}

>From f366d8baedcaa2169094616bf79e795797e5c62a Mon Sep 17 00:00:00 2001
From: Berke-Ates <berke at ates.ch>
Date: Thu, 30 Jul 2026 07:15:41 +0200
Subject: [PATCH 2/4] [MLIR][Arith] Added additional test and simplified
 BitcastOp fold bugfix

---
 mlir/lib/Dialect/Arith/IR/ArithOps.cpp    |  4 +---
 mlir/test/Dialect/Arith/canonicalize.mlir | 12 ++++++++++++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 14cc8718d51a5..e6080a0b3f04e 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -2284,9 +2284,7 @@ OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
   if (auto resFloatType = dyn_cast<FloatType>(resType))
     return FloatAttr::get(resType,
                           APFloat(resFloatType.getFloatSemantics(), bits));
-  if (auto resIntType = dyn_cast<IntegerType>(resType))
-    return IntegerAttr::get(resType, bits);
-  return {};
+  return IntegerAttr::get(resType, bits);
 }
 
 void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index d6ea02e5508dd..0c06aa6e861a4 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -2651,6 +2651,18 @@ func.func @bitcastChain(%arg: i16) -> f16 {
 
 // -----
 
+// CHECK-LABEL: func @bitcastForeignConstantAttr
+func.func @bitcastForeignConstantAttr() -> f64 {
+  // CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : i64
+  // CHECK: %[[CAST:.*]] = arith.bitcast %[[UNDEF]] : i64 to f64
+  // CHECK: return %[[CAST]] : f64
+  %0 = llvm.mlir.undef : i64
+  %1 = arith.bitcast %0 : i64 to f64
+  return %1 : f64
+}
+
+// -----
+
 // CHECK-LABEL: test_maxsi
 // CHECK-DAG: %[[C0:.+]] = arith.constant 42
 // CHECK-DAG: %[[MAX_INT_CST:.+]] = arith.constant 127

>From 465ef11461558e78808531287cc06fb1fcc4b6b2 Mon Sep 17 00:00:00 2001
From: Berke-Ates <berke at ates.ch>
Date: Thu, 30 Jul 2026 08:10:39 +0200
Subject: [PATCH 3/4] Removed SCCP testcase

---
 mlir/test/Transforms/sccp.mlir | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/mlir/test/Transforms/sccp.mlir b/mlir/test/Transforms/sccp.mlir
index b7ce60320d465..251a74dc20647 100644
--- a/mlir/test/Transforms/sccp.mlir
+++ b/mlir/test/Transforms/sccp.mlir
@@ -335,19 +335,3 @@ func.func @fold_to_non_operand_value(%x: i64, %cond: i1) -> i64 {
   %cast2 = builtin.unrealized_conversion_cast %cast1 : index to i64
   return %cast2 : i64
 }
-
-// -----
-
-// SCCP propagates the constant attribute produced by llvm.mlir.undef's fold
-// (an LLVM::UndefAttr) into arith.bitcast's fold, which must gracefully bail
-// instead of asserting.
-
-// CHECK-LABEL: func @bitcast_of_foreign_constant_attr
-func.func @bitcast_of_foreign_constant_attr() -> f64 {
-  // CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : i64
-  // CHECK: %[[CAST:.*]] = arith.bitcast %[[UNDEF]] : i64 to f64
-  // CHECK: return %[[CAST]] : f64
-  %0 = llvm.mlir.undef : i64
-  %1 = arith.bitcast %0 : i64 to f64
-  return %1 : f64
-}

>From 01973116c46f2bd9e54b77057c6307686251d148 Mon Sep 17 00:00:00 2001
From: Berke-Ates <berke at ates.ch>
Date: Mon, 3 Aug 2026 10:02:02 +0200
Subject: [PATCH 4/4] Added lambda expression for variable initialization

---
 mlir/lib/Dialect/Arith/IR/ArithOps.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index e6080a0b3f04e..b261a37eaab57 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -2270,14 +2270,15 @@ OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
     return ub::PoisonAttr::get(getContext());
 
   /// Bitcast integer or float to integer or float.
-  APInt bits;
-  if (auto floatAttr = dyn_cast<FloatAttr>(operand))
-    bits = floatAttr.getValue().bitcastToAPInt();
-  else if (auto intAttr = dyn_cast<IntegerAttr>(operand))
-    bits = intAttr.getValue();
-  else
+  if (!llvm::isa<FloatAttr, IntegerAttr>(operand))
     return {};
 
+  const APInt bits = [&]() {
+    if (auto floatAttr = dyn_cast<FloatAttr>(operand))
+      return floatAttr.getValue().bitcastToAPInt();
+    return cast<IntegerAttr>(operand).getValue();
+  }();
+
   assert(resType.getIntOrFloatBitWidth() == bits.getBitWidth() &&
          "trying to fold on broken IR: operands have incompatible types");
 



More information about the Mlir-commits mailing list