[Mlir-commits] [mlir] [mlir][arith] fold valid int2fp2int round trips for canonicalizations (PR #233860)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Oct 21 20:56:13 PDT 2026


https://github.com/dashygo097 updated https://github.com/llvm/llvm-project/pull/233860

>From dcb82ea088ef3b6b4568bf0b178bd1054171fcfc Mon Sep 17 00:00:00 2001
From: dashygo097 <dashygo097 at gmail.com>
Date: Tue, 20 Oct 2026 21:49:50 +0800
Subject: [PATCH] [mlir][arith] fold valid int2fp2int round trips for
 canonicalizations

---
 mlir/lib/Dialect/Arith/IR/ArithOps.cpp    | 44 ++++++++++-
 mlir/test/Dialect/Arith/canonicalize.mlir | 91 +++++++++++++++++++++++
 2 files changed, 133 insertions(+), 2 deletions(-)

diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index be02143ea27711..f3cf58bb074065 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1346,7 +1346,6 @@ void arith::SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
 }
 
 namespace {
-
 /// Narrow an extremum whose operands were extended from the result type:
 ///
 ///   trunc(extremum(ext(lhs), ext(rhs))) -> extremum(lhs, rhs)
@@ -1409,7 +1408,6 @@ struct NarrowExtremum final : OpRewritePattern<TruncOp> {
     return success();
   }
 };
-
 } // namespace
 
 //===----------------------------------------------------------------------===//
@@ -2244,6 +2242,38 @@ static bool checkIntFloatCast(TypeRange inputs, TypeRange outputs) {
   return srcType && dstType;
 }
 
+//===----------------------------------------------------------------------===//
+// NOTE: TO BE WRITTEN
+//===----------------------------------------------------------------------===//
+template <typename IntToFPOp, bool IsSigned>
+static Value foldIntToFPToInt(Value input, Type resultType) {
+  auto conversion = input.getDefiningOp<IntToFPOp>();
+  if (!conversion || conversion.getIn().getType() != resultType) {
+    return {};
+  }
+
+  auto integer = llvm::cast<IntegerType>(getElementTypeOrSelf(resultType));
+  auto floating = llvm::cast<FloatType>(getElementTypeOrSelf(input.getType()));
+  const auto &semantics = floating.getFloatSemantics();
+  unsigned width = integer.getWidth();
+
+  // Every source integer must be representable, including zero and,
+  // for signed conversions, negative values.
+  if (width == 0 || !APFloat::semanticsHasZero(semantics) ||
+      (IsSigned && !APFloat::semanticsHasSignedRepr(semantics))) {
+    return {};
+  }
+
+  unsigned precisionNeeded = width - (IsSigned ? 1U : 0U);
+  if (precisionNeeded > APFloat::semanticsPrecision(semantics) ||
+      static_cast<int64_t>(width - 1) >
+          APFloat::semanticsMaxExponent(semantics)) {
+    return {};
+  }
+
+  return conversion.getIn();
+}
+
 //===----------------------------------------------------------------------===//
 // UIToFPOp
 //===----------------------------------------------------------------------===//
@@ -2307,6 +2337,11 @@ bool arith::FPToUIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
 }
 
 OpFoldResult arith::FPToUIOp::fold(FoldAdaptor adaptor) {
+  if (auto original =
+          foldIntToFPToInt<arith::UIToFPOp, false>(getIn(), getType())) {
+    return original;
+  }
+
   Type resType = getElementTypeOrSelf(getType());
   unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
   return constFoldCastOp<FloatAttr, IntegerAttr>(
@@ -2329,6 +2364,11 @@ bool arith::FPToSIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
 }
 
 OpFoldResult arith::FPToSIOp::fold(FoldAdaptor adaptor) {
+  if (auto original =
+          foldIntToFPToInt<arith::SIToFPOp, true>(getIn(), getType())) {
+    return original;
+  }
+
   Type resType = getElementTypeOrSelf(getType());
   unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
   return constFoldCastOp<FloatAttr, IntegerAttr>(
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 9c512cd605d7d8..4610413d927418 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -3875,6 +3875,97 @@ func.func @constant_UItoFP_vector() -> vector<4xf32> {
   return %res : vector<4xf32>
 }
 
+// -----
+// f32 exactly represents signed i25, but not every signed i26.
+// CHECK-LABEL: @int_fp_roundtrip_signed_boundary(
+func.func @int_fp_roundtrip_signed_boundary(%x: i25, %y: i26) -> (i25, i26) {
+  // CHECK-SAME: %[[X:[^:]+]]: i25, %[[Y:[^:]+]]: i26
+  // CHECK-NEXT: %[[F:.*]] = arith.sitofp %[[Y]] : i26 to f32
+  // CHECK-NEXT: %[[R:.*]] = arith.fptosi %[[F]] : f32 to i26
+  // CHECK-NEXT: return %[[X]], %[[R]] : i25, i26
+  %fx = arith.sitofp %x : i25 to f32
+  %rx = arith.fptosi %fx : f32 to i25
+  %fy = arith.sitofp %y : i26 to f32
+  %ry = arith.fptosi %fy : f32 to i26
+  return %rx, %ry : i25, i26
+}
+
+// f32 exactly represents unsigned i24, but not every unsigned i25.
+// CHECK-LABEL: @int_fp_roundtrip_unsigned_boundary(
+func.func @int_fp_roundtrip_unsigned_boundary(%x: i24, %y: i25) -> (i24, i25) {
+  // CHECK-SAME: %[[X:[^:]+]]: i24, %[[Y:[^:]+]]: i25
+  // CHECK-NEXT: %[[F:.*]] = arith.uitofp %[[Y]] : i25 to f32
+  // CHECK-NEXT: %[[R:.*]] = arith.fptoui %[[F]] : f32 to i25
+  // CHECK-NEXT: return %[[X]], %[[R]] : i24, i25
+  %fx = arith.uitofp %x : i24 to f32
+  %rx = arith.fptoui %fx : f32 to i24
+  %fy = arith.uitofp %y : i25 to f32
+  %ry = arith.fptoui %fy : f32 to i25
+  return %rx, %ry : i24, i25
+}
+
+// Other precisions and a finite-only FP6 format use the same proof.
+// CHECK-LABEL: @int_fp_roundtrip_other_formats(
+func.func @int_fp_roundtrip_other_formats(%x: i12, %y: i53, %z: i3) -> (i12, i53, i3) {
+  // CHECK-SAME: %[[X:[^:]+]]: i12, %[[Y:[^:]+]]: i53, %[[Z:[^:]+]]: i3
+  // CHECK-NEXT: return %[[X]], %[[Y]], %[[Z]] : i12, i53, i3
+  %fx = arith.sitofp %x : i12 to f16
+  %rx = arith.fptosi %fx : f16 to i12
+  %fy = arith.uitofp %y : i53 to f64
+  %ry = arith.fptoui %fy : f64 to i53
+  %fz = arith.uitofp %z : i3 to f6E2M3FN
+  %rz = arith.fptoui %fz : f6E2M3FN to i3
+  return %rx, %ry, %rz : i12, i53, i3
+}
+
+// -----
+// FP6 precision suffices for i4, but its range excludes signed -8 and unsigned 15.
+// CHECK-LABEL: @int_fp_roundtrip_exponent(
+func.func @int_fp_roundtrip_exponent(%x: i4) -> (i4, i4) {
+  // CHECK-SAME: %[[X:[^:]+]]: i4
+  // CHECK-NEXT: %[[SF:.*]] = arith.sitofp %[[X]] : i4 to f6E2M3FN
+  // CHECK-NEXT: %[[SR:.*]] = arith.fptosi %[[SF]] : f6E2M3FN to i4
+  // CHECK-NEXT: %[[UF:.*]] = arith.uitofp %[[X]] : i4 to f6E2M3FN
+  // CHECK-NEXT: %[[UR:.*]] = arith.fptoui %[[UF]] : f6E2M3FN to i4
+  // CHECK-NEXT: return %[[SR]], %[[UR]] : i4, i4
+  %sf = arith.sitofp %x : i4 to f6E2M3FN
+  %sr = arith.fptosi %sf : f6E2M3FN to i4
+  %uf = arith.uitofp %x : i4 to f6E2M3FN
+  %ur = arith.fptoui %uf : f6E2M3FN to i4
+  return %sr, %ur : i4, i4
+}
+
+// Zero is required; signed inputs require negatives; unsigned FP can serve unsigned inputs.
+// CHECK-LABEL: @int_fp_roundtrip_special_formats(
+func.func @int_fp_roundtrip_special_formats(%x: i1, %y: i2, %z: i4) -> (i1, i2, i4) {
+  // CHECK-SAME: %[[X:[^:]+]]: i1, %[[Y:[^:]+]]: i2, %[[Z:[^:]+]]: i4
+  // CHECK-NEXT: %[[XF:.*]] = arith.uitofp %[[X]] : i1 to f8E8M0FNU
+  // CHECK-NEXT: %[[XR:.*]] = arith.fptoui %[[XF]] : f8E8M0FNU to i1
+  // CHECK-NEXT: %[[YF:.*]] = arith.sitofp %[[Y]] : i2 to f8E5M3FNU
+  // CHECK-NEXT: %[[YR:.*]] = arith.fptosi %[[YF]] : f8E5M3FNU to i2
+  // CHECK-NEXT: return %[[XR]], %[[YR]], %[[Z]] : i1, i2, i4
+  %xf = arith.uitofp %x : i1 to f8E8M0FNU
+  %xr = arith.fptoui %xf : f8E8M0FNU to i1
+  %yf = arith.sitofp %y : i2 to f8E5M3FNU
+  %yr = arith.fptosi %yf : f8E5M3FNU to i2
+  %zf = arith.uitofp %z : i4 to f8E5M3FNU
+  %zr = arith.fptoui %zf : f8E5M3FNU to i4
+  return %xr, %yr, %zr : i1, i2, i4
+}
+
+// -----
+// Folding uses element types for both vectors and tensors.
+// CHECK-LABEL: @int_fp_roundtrip_shaped(
+func.func @int_fp_roundtrip_shaped(%x: vector<4xi32>, %y: tensor<2x3xi16>) -> (vector<4xi32>, tensor<2x3xi16>) {
+  // CHECK-SAME: %[[X:[^:]+]]: vector<4xi32>, %[[Y:[^:]+]]: tensor<2x3xi16>
+  // CHECK-NEXT: return %[[X]], %[[Y]] : vector<4xi32>, tensor<2x3xi16>
+  %xf = arith.sitofp %x : vector<4xi32> to vector<4xf64>
+  %xr = arith.fptosi %xf : vector<4xf64> to vector<4xi32>
+  %yf = arith.uitofp %y : tensor<2x3xi16> to tensor<2x3xf32>
+  %yr = arith.fptoui %yf : tensor<2x3xf32> to tensor<2x3xi16>
+  return %xr, %yr : vector<4xi32>, tensor<2x3xi16>
+}
+
 // -----
 
 // Tests rewritten from https://github.com/llvm/llvm-project/blob/main/llvm/test/Transforms/InstCombine/2008-11-08-FCmp.ll



More information about the Mlir-commits mailing list