[Mlir-commits] [mlir] [mlir][math] Add opt-in f64 round expansion (PR #230408)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Oct 9 01:48:19 PDT 2026


https://github.com/sriramshastry updated https://github.com/llvm/llvm-project/pull/230408

>From d4317464b565be091617f592c061b2d170b0166f Mon Sep 17 00:00:00 2001
From: Sriram Shastry <sriramshastry at gmail.com>
Date: Fri, 9 Oct 2026 08:36:15 +0000
Subject: [PATCH 1/2] [mlir][math] Add opt-in f64 round expansion

Signed-off-by: Sriram Shastry <sriramshastry at gmail.com>
---
 .../mlir/Dialect/Math/Transforms/Passes.td    |  12 +-
 .../lib/Dialect/Math/Transforms/ExpandOps.cpp |  62 +++++
 .../Conversion/MathToLLVM/math-to-llvm.mlir   |  33 +++
 mlir/test/Dialect/Math/expand-math.mlir       |  51 +++++
 .../Dialect/Math/round-f64-expansion.mlir     | 211 ++++++++++++++++++
 5 files changed, 368 insertions(+), 1 deletion(-)
 create mode 100644 mlir/test/Dialect/Math/round-f64-expansion.mlir

diff --git a/mlir/include/mlir/Dialect/Math/Transforms/Passes.td b/mlir/include/mlir/Dialect/Math/Transforms/Passes.td
index 56ba4d05caa5a2..1095a7e07c7c1e 100644
--- a/mlir/include/mlir/Dialect/Math/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/Math/Transforms/Passes.td
@@ -51,6 +51,14 @@ def MathExpandOpsPass : Pass<"math-expand-ops"> {
     to be subsequently lowered through these. For example, hyperbolic functions
     are transformed into their expanded form containing only `exp` functions.
 
+    f64 `math.round` expansion is disabled by default. Set `expand-round-f64`
+    to enable its integer software expansion when `round` is selected by `ops`.
+    This includes Round operations generated by RoundEven expansion. Default
+    behavior, f32 lowering and direct LLVM conversion are unchanged. Prefer
+    direct LLVM lowering when native rounding instructions are available;
+    expansion assumes the default floating-point environment and does not
+    preserve observable exception flags or traps.
+
     The `ops` parameter can be used to apply only a subset of all the
     available expansions, these must correspond to the operation mnemonic.
     For example, `ops=sinh,acosh` will expand only `math.sinh` and
@@ -60,7 +68,9 @@ def MathExpandOpsPass : Pass<"math-expand-ops"> {
   let dependentDialects = ["arith::ArithDialect", "mlir::tensor::TensorDialect"];
   let options = [
     ListOption<"opMnemonics", "ops", "std::string",
-               "Operations to expand.">
+               "Operations to expand.">,
+    Option<"expandRoundF64", "expand-round-f64", "bool", "false",
+           "Enable f64 round expansion when round is selected">
   ];
 }
 
diff --git a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
index 41816abf073398..f0ec498b737432 100644
--- a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
+++ b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
@@ -518,6 +518,65 @@ static LogicalResult convertExp2fOp(math::Exp2Op op,
   return success();
 }
 
+static LogicalResult convertRoundF64Op(math::RoundOp op,
+                                      PatternRewriter &rewriter) {
+  Location loc = op.getLoc();
+  ImplicitLocOpBuilder b(loc, rewriter);
+  Value operand = op.getOperand();
+  Type opType = operand.getType();
+  Type opEType = getElementTypeOrSelf(opType);
+
+  if (!opEType.isF64() || isUnrankedShaped(opType))
+    return failure();
+
+  Type intType = b.getI64Type();
+  if (auto shapedType = dyn_cast<ShapedType>(opType))
+    intType = shapedType.clone(intType);
+
+  auto intConst = [&](int64_t value) {
+    return createIntConst(loc, intType, value, b, operand);
+  };
+
+  Value zero = intConst(0);
+  Value one = intConst(1);
+  Value allOnes = intConst(-1);
+  Value cFractionBits = intConst(52);
+  Value cBias = intConst(1023);
+  Value cRoundShiftBase = intConst(51);
+  Value cShiftMask = intConst(63);
+  Value magnitudeMask = intConst(0x7fffffffffffffffLL);
+  Value halfBits = intConst(0x3fe0000000000000LL);
+  Value oneBits = intConst(0x3ff0000000000000LL);
+
+  Value bits = arith::BitcastOp::create(b, intType, operand);
+  Value magnitude = arith::AndIOp::create(b, bits, magnitudeMask);
+  Value sign = arith::XOrIOp::create(b, bits, magnitude);
+  Value exponentField = arith::ShRUIOp::create(b, magnitude, cFractionBits);
+  Value exponent = arith::SubIOp::create(b, exponentField, cBias);
+  Value roundShift = arith::SubIOp::create(b, cRoundShiftBase, exponent);
+  roundShift = arith::AndIOp::create(b, roundShift, cShiftMask);
+  Value roundBit = arith::ShLIOp::create(b, one, roundShift);
+  Value mask =
+      arith::SubIOp::create(b, arith::ShLIOp::create(b, roundBit, one), one);
+  Value invertedMask = arith::XOrIOp::create(b, mask, allOnes);
+  Value incremented = arith::AddIOp::create(b, magnitude, roundBit);
+  Value roundedMagnitude = arith::AndIOp::create(b, incremented, invertedMask);
+  Value atLeastHalf =
+      arith::CmpIOp::create(b, arith::CmpIPredicate::uge, magnitude, halfBits);
+  Value smallMagnitude = arith::SelectOp::create(b, atLeastHalf, oneBits, zero);
+  Value isSmall =
+      arith::CmpIOp::create(b, arith::CmpIPredicate::slt, exponent, zero);
+  Value resultMagnitude =
+      arith::SelectOp::create(b, isSmall, smallMagnitude, roundedMagnitude);
+  Value isLarge = arith::CmpIOp::create(b, arith::CmpIPredicate::sge, exponent,
+                                      cFractionBits);
+  resultMagnitude =
+      arith::SelectOp::create(b, isLarge, magnitude, resultMagnitude);
+  Value resultBits = arith::OrIOp::create(b, resultMagnitude, sign);
+  rewriter.replaceOp(op, arith::BitcastOp::create(b, opType, resultBits));
+  return success();
+}
+
 static LogicalResult convertRoundOp(math::RoundOp op,
                                     PatternRewriter &rewriter) {
   Location loc = op.getLoc();
@@ -860,6 +919,9 @@ struct MathExpandOpsPass final
     SmallVector<StringRef> mnemonics =
         llvm::to_vector_of<StringRef>(opMnemonics);
     math::populateExpansionPatterns(patterns, mnemonics);
+    if (expandRoundF64 &&
+        (mnemonics.empty() || llvm::count(mnemonics, "round") > 0))
+      patterns.add(convertRoundF64Op);
     if (failed(applyPatternsGreedily(getOperation(), std::move(patterns))))
       return signalPassFailure();
   }
diff --git a/mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir b/mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir
index 3dd9d74ba7043e..4dacba6c78085a 100644
--- a/mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir
+++ b/mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir
@@ -654,6 +654,39 @@ func.func @round(%arg0 : f32) {
 
 // -----
 
+// CHECK-LABEL: func @round_f64(
+// CHECK-SAME: %[[ARG:.*]]: f64
+func.func @round_f64(%arg: f64) -> f64 {
+  // CHECK: %[[RESULT:.*]] = llvm.intr.round(%[[ARG]]) : (f64) -> f64
+  // CHECK: return %[[RESULT]]
+  %result = math.round %arg : f64
+  return %result : f64
+}
+
+// -----
+
+// CHECK-LABEL: func @round_vector_f64(
+// CHECK-SAME: %[[ARG:.*]]: vector<2xf64>
+func.func @round_vector_f64(%arg: vector<2xf64>) -> vector<2xf64> {
+  // CHECK: %[[RESULT:.*]] = llvm.intr.round(%[[ARG]]) : (vector<2xf64>) -> vector<2xf64>
+  // CHECK: return %[[RESULT]]
+  %result = math.round %arg : vector<2xf64>
+  return %result : vector<2xf64>
+}
+
+// -----
+
+// CHECK-LABEL: func @round_scalable_f64(
+// CHECK-SAME: %[[ARG:.*]]: vector<[2]xf64>
+func.func @round_scalable_f64(%arg: vector<[2]xf64>) -> vector<[2]xf64> {
+  // CHECK: %[[RESULT:.*]] = llvm.intr.round(%[[ARG]]) : (vector<[2]xf64>) -> vector<[2]xf64>
+  // CHECK: return %[[RESULT]]
+  %result = math.round %arg : vector<[2]xf64>
+  return %result : vector<[2]xf64>
+}
+
+// -----
+
 // CHECK-LABEL: func @roundeven(
 // CHECK-SAME: f32
 func.func @roundeven(%arg0 : f32) {
diff --git a/mlir/test/Dialect/Math/expand-math.mlir b/mlir/test/Dialect/Math/expand-math.mlir
index 84b0bb0aa314ae..dcd4bfedf79a5f 100644
--- a/mlir/test/Dialect/Math/expand-math.mlir
+++ b/mlir/test/Dialect/Math/expand-math.mlir
@@ -1,5 +1,6 @@
 // RUN: mlir-opt %s --split-input-file -math-expand-ops | FileCheck %s
 // RUN: mlir-opt %s --split-input-file -math-expand-ops=ops=tanh,tan | FileCheck %s --check-prefix=CHECK-FILTER
+// RUN: mlir-opt %s --split-input-file -math-expand-ops=expand-round-f64=true | FileCheck %s --check-prefix=F64
 
 // CHECK-LABEL: func @tanh
 func.func @tanh(%arg: f32) -> f32 {
@@ -254,6 +255,52 @@ func.func @roundf_func(%a: f32) -> f32 {
 
 // -----
 
+// CHECK-LABEL: func @round_f64_dynamic_tensor
+// CHECK: math.round
+// CHECK: return
+// F64-LABEL: func @round_f64_dynamic_tensor
+// F64: tensor.dim
+// F64: tensor.splat
+// F64: arith.bitcast %{{.*}} : tensor<?xf64> to tensor<?xi64>
+// F64: arith.andi
+// F64: arith.shli
+// F64-NOT: math.round
+// F64-NOT: scf.if
+// F64: return
+func.func @round_f64_dynamic_tensor(%arg: tensor<?xf64>) -> tensor<?xf64> {
+  %result = math.round %arg : tensor<?xf64>
+  return %result : tensor<?xf64>
+}
+
+// -----
+
+// CHECK-LABEL: func @round_f64_tensor
+// CHECK: math.round
+// CHECK: return
+// F64-LABEL: func @round_f64_tensor
+// F64: arith.bitcast %{{.*}} : tensor<2xf64> to tensor<2xi64>
+// F64: arith.shli
+// F64-NOT: math.round
+// F64-NOT: scf.if
+// F64: return
+func.func @round_f64_tensor(%arg: tensor<2xf64>) -> tensor<2xf64> {
+  %result = math.round %arg : tensor<2xf64>
+  return %result : tensor<2xf64>
+}
+
+// -----
+
+// CHECK-LABEL: func @round_f64_unranked
+// CHECK: math.round
+// F64-LABEL: func @round_f64_unranked
+// F64: math.round
+func.func @round_f64_unranked(%arg: tensor<*xf64>) -> tensor<*xf64> {
+  %result = math.round %arg : tensor<*xf64>
+  return %result : tensor<*xf64>
+}
+
+// -----
+
 // CHECK-LABEL:   func @powf_func
 // CHECK-SAME:    (%[[ARG0:.+]]: f64, %[[ARG1:.+]]: f64) -> f64
 func.func @powf_func(%a: f64, %b: f64) -> f64 {
@@ -353,6 +400,10 @@ func.func @powf_func_three(%a: f64) -> f64{
 // -----
 
 // CHECK-LABEL:   func.func @roundeven64
+// F64-LABEL: func.func @roundeven64
+// F64: arith.bitcast %{{.*}} : i64 to f64
+// F64-NOT: math.round
+// F64: return
 func.func @roundeven64(%arg: f64) -> f64 {
   %res = math.roundeven %arg : f64
   return %res : f64
diff --git a/mlir/test/Dialect/Math/round-f64-expansion.mlir b/mlir/test/Dialect/Math/round-f64-expansion.mlir
new file mode 100644
index 00000000000000..6593e98bd8d9f4
--- /dev/null
+++ b/mlir/test/Dialect/Math/round-f64-expansion.mlir
@@ -0,0 +1,211 @@
+// RUN: mlir-opt %s -math-expand-ops | FileCheck %s --check-prefix=DEFAULT
+// RUN: mlir-opt %s -math-expand-ops=expand-round-f64=false | FileCheck %s --check-prefix=DEFAULT
+// RUN: mlir-opt %s -math-expand-ops="ops=roundeven expand-round-f64=true" | FileCheck %s --check-prefix=DEFAULT
+// RUN: mlir-opt %s -math-expand-ops="ops=round expand-round-f64=true" | FileCheck %s --check-prefix=ROUND-ONLY
+// RUN: mlir-opt %s -math-expand-ops=expand-round-f64=true | FileCheck %s --check-prefix=CHECK --enable-var-scope --implicit-check-not=scf.if --implicit-check-not=math.round
+// RUN: mlir-opt %s -math-expand-ops=expand-round-f64=true -inline -canonicalize | FileCheck %s --check-prefix=VALUES --enable-var-scope
+// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(math-expand-ops{expand-round-f64=true},convert-math-to-llvm,convert-arith-to-llvm),convert-vector-to-llvm,convert-func-to-llvm,reconcile-unrealized-casts)' | FileCheck %s --check-prefix=LLVM --enable-var-scope --implicit-check-not=scf.if --implicit-check-not=llvm.cond_br
+
+// DEFAULT-LABEL: func.func @round_f64(
+// DEFAULT: math.round
+// ROUND-ONLY-LABEL: func.func @round_f64(
+// ROUND-ONLY: arith.shli
+// ROUND-ONLY-NOT: math.round
+// ROUND-ONLY: return
+// CHECK-LABEL: func.func @round_f64(
+// CHECK-DAG: %[[MASK:.*]] = arith.constant 63 : i64
+// CHECK: %[[SHIFT:.*]] = arith.andi %{{.*}}, %[[MASK]] : i64
+// CHECK: arith.shli %{{.*}}, %[[SHIFT]] : i64
+// CHECK: arith.select
+// CHECK: arith.bitcast %{{.*}} : i64 to f64
+// LLVM-LABEL: llvm.func @round_f64(
+// LLVM: llvm.shl
+// LLVM: llvm.select
+func.func @round_f64(%value: f64) -> f64 {
+  %result = math.round %value : f64
+  return %result : f64
+}
+
+// DEFAULT-LABEL: func.func @round_vector_f64(
+// DEFAULT: math.round
+// ROUND-ONLY-LABEL: func.func @round_vector_f64(
+// ROUND-ONLY: arith.shli
+// ROUND-ONLY-NOT: math.round
+// ROUND-ONLY: return
+// CHECK-LABEL: func.func @round_vector_f64(
+// CHECK-DAG: %[[MASK:.*]] = arith.constant dense<63> : vector<2xi64>
+// CHECK: %[[SHIFT:.*]] = arith.andi %{{.*}}, %[[MASK]] : vector<2xi64>
+// CHECK: arith.shli %{{.*}}, %[[SHIFT]] : vector<2xi64>
+// LLVM-LABEL: llvm.func @round_vector_f64(
+// LLVM: llvm.shl
+// LLVM: llvm.select
+func.func @round_vector_f64(%value: vector<2xf64>) -> vector<2xf64> {
+  %result = math.round %value : vector<2xf64>
+  return %result : vector<2xf64>
+}
+
+// DEFAULT-LABEL: func.func @round_scalable_f64(
+// DEFAULT: math.round
+// ROUND-ONLY-LABEL: func.func @round_scalable_f64(
+// ROUND-ONLY: arith.shli
+// ROUND-ONLY-NOT: math.round
+// ROUND-ONLY: return
+// CHECK-LABEL: func.func @round_scalable_f64(
+// CHECK-DAG: %[[MASK:.*]] = arith.constant dense<63> : vector<[2]xi64>
+// CHECK: %[[SHIFT:.*]] = arith.andi %{{.*}}, %[[MASK]] : vector<[2]xi64>
+// CHECK: arith.shli %{{.*}}, %[[SHIFT]] : vector<[2]xi64>
+// LLVM-LABEL: llvm.func @round_scalable_f64(
+// LLVM: llvm.shl
+// LLVM: llvm.select
+func.func @round_scalable_f64(%value: vector<[2]xf64>) -> vector<[2]xf64> {
+  %result = math.round %value : vector<[2]xf64>
+  return %result : vector<[2]xf64>
+}
+
+// LLVM-LABEL: llvm.func @masked_f64(
+// LLVM-SAME: %[[CHOOSE:[^ :]+]]: i1
+// LLVM: llvm.select %[[CHOOSE]],
+// LLVM: llvm.return
+func.func @masked_f64(%choose: i1, %value: f64) -> f64 {
+  %rounded = math.round %value : f64
+  %zero = arith.constant 0.0 : f64
+  %result = arith.select %choose, %rounded, %zero : f64
+  return %result : f64
+}
+
+// LLVM-LABEL: llvm.func @masked_vector_f64(
+// LLVM-SAME: %[[CHOOSE:[^ :]+]]: i1
+// LLVM: llvm.select %[[CHOOSE]],
+// LLVM: llvm.return
+func.func @masked_vector_f64(%choose: i1, %value: vector<2xf64>) -> vector<2xf64> {
+  %rounded = math.round %value : vector<2xf64>
+  %zero = arith.constant dense<0.0> : vector<2xf64>
+  %result = arith.select %choose, %rounded, %zero : vector<2xf64>
+  return %result : vector<2xf64>
+}
+
+// LLVM-LABEL: llvm.func @masked_scalable_f64(
+// LLVM-SAME: %[[CHOOSE:[^ :]+]]: i1
+// LLVM: llvm.select %[[CHOOSE]],
+// LLVM: llvm.return
+func.func @masked_scalable_f64(%choose: i1, %value: vector<[2]xf64>) -> vector<[2]xf64> {
+  %rounded = math.round %value : vector<[2]xf64>
+  %zero = arith.constant dense<0.0> : vector<[2]xf64>
+  %result = arith.select %choose, %rounded, %zero : vector<[2]xf64>
+  return %result : vector<[2]xf64>
+}
+
+// VALUES-LABEL: func.func @half_neighbors(
+// VALUES: %[[EXPECTED:.*]] = arith.constant dense<[0, 4607182418800017408]> : vector<2xi64>
+// VALUES-NEXT: return %[[EXPECTED]] : vector<2xi64>
+func.func @half_neighbors() -> vector<2xi64> {
+  %input = arith.constant dense<[4602678819172646911, 4602678819172646912]> : vector<2xi64>
+  %value = arith.bitcast %input : vector<2xi64> to vector<2xf64>
+  %rounded = func.call @round_vector_f64(%value) : (vector<2xf64>) -> vector<2xf64>
+  %result = arith.bitcast %rounded : vector<2xf64> to vector<2xi64>
+  return %result : vector<2xi64>
+}
+
+// VALUES-LABEL: func.func @ties(
+// VALUES: %[[EXPECTED:.*]] = arith.constant dense<[4611686018427387904, -4609434218613702656]> : vector<2xi64>
+// VALUES-NEXT: return %[[EXPECTED]] : vector<2xi64>
+func.func @ties() -> vector<2xi64> {
+  %input = arith.constant dense<[4609434218613702656, -4610560118520545280]> : vector<2xi64>
+  %value = arith.bitcast %input : vector<2xi64> to vector<2xf64>
+  %rounded = func.call @round_vector_f64(%value) : (vector<2xf64>) -> vector<2xf64>
+  %result = arith.bitcast %rounded : vector<2xf64> to vector<2xi64>
+  return %result : vector<2xi64>
+}
+
+// VALUES-LABEL: func.func @signed_subnormals(
+// VALUES: %[[EXPECTED:.*]] = arith.constant dense<[0, -9223372036854775808]> : vector<2xi64>
+// VALUES-NEXT: return %[[EXPECTED]] : vector<2xi64>
+func.func @signed_subnormals() -> vector<2xi64> {
+  %input = arith.constant dense<[1, -9223372036854775807]> : vector<2xi64>
+  %value = arith.bitcast %input : vector<2xi64> to vector<2xf64>
+  %rounded = func.call @round_vector_f64(%value) : (vector<2xf64>) -> vector<2xf64>
+  %result = arith.bitcast %rounded : vector<2xf64> to vector<2xi64>
+  return %result : vector<2xi64>
+}
+
+// VALUES-LABEL: func.func @large_boundary(
+// VALUES: %[[EXPECTED:.*]] = arith.constant dense<[4841369599423283200, 4841369599423283201]> : vector<2xi64>
+// VALUES-NEXT: return %[[EXPECTED]] : vector<2xi64>
+func.func @large_boundary() -> vector<2xi64> {
+  %input = arith.constant dense<[4841369599423283199, 4841369599423283201]> : vector<2xi64>
+  %value = arith.bitcast %input : vector<2xi64> to vector<2xf64>
+  %rounded = func.call @round_vector_f64(%value) : (vector<2xf64>) -> vector<2xf64>
+  %result = arith.bitcast %rounded : vector<2xf64> to vector<2xi64>
+  return %result : vector<2xi64>
+}
+
+// VALUES-LABEL: func.func @special_values(
+// VALUES: %[[EXPECTED:.*]] = arith.constant dense<[9218868437227405312, 9221120237041095220]> : vector<2xi64>
+// VALUES-NEXT: return %[[EXPECTED]] : vector<2xi64>
+func.func @special_values() -> vector<2xi64> {
+  %input = arith.constant dense<[9218868437227405312, 9221120237041095220]> : vector<2xi64>
+  %value = arith.bitcast %input : vector<2xi64> to vector<2xf64>
+  %rounded = func.call @round_vector_f64(%value) : (vector<2xf64>) -> vector<2xf64>
+  %result = arith.bitcast %rounded : vector<2xf64> to vector<2xi64>
+  return %result : vector<2xi64>
+}
+
+// VALUES-LABEL: func.func @negative_zero(
+// VALUES: %[[EXPECTED:.*]] = arith.constant -9223372036854775808 : i64
+// VALUES-NEXT: return %[[EXPECTED]] : i64
+func.func @negative_zero() -> i64 {
+  %input = arith.constant -9223372036854775808 : i64
+  %value = arith.bitcast %input : i64 to f64
+  %rounded = func.call @round_f64(%value) : (f64) -> f64
+  %result = arith.bitcast %rounded : f64 to i64
+  return %result : i64
+}
+
+// VALUES-LABEL: func.func @negative_infinity(
+// VALUES: %[[EXPECTED:.*]] = arith.constant -4503599627370496 : i64
+// VALUES-NEXT: return %[[EXPECTED]] : i64
+func.func @negative_infinity() -> i64 {
+  %input = arith.constant -4503599627370496 : i64
+  %value = arith.bitcast %input : i64 to f64
+  %rounded = func.call @round_f64(%value) : (f64) -> f64
+  %result = arith.bitcast %rounded : f64 to i64
+  return %result : i64
+}
+
+// VALUES-LABEL: func.func @signaling_nan(
+// VALUES: %[[EXPECTED:.*]] = arith.constant 9218868437227405313 : i64
+// VALUES-NEXT: return %[[EXPECTED]] : i64
+func.func @signaling_nan() -> i64 {
+  %input = arith.constant 9218868437227405313 : i64
+  %value = arith.bitcast %input : i64 to f64
+  %rounded = func.call @round_f64(%value) : (f64) -> f64
+  %result = arith.bitcast %rounded : f64 to i64
+  return %result : i64
+}
+
+// DEFAULT-LABEL: func.func @roundeven_f64(
+// ROUND-ONLY-LABEL: func.func @roundeven_f64(
+// ROUND-ONLY: math.roundeven
+// DEFAULT: math.round
+// DEFAULT-NOT: math.roundeven
+// DEFAULT: return
+// CHECK-LABEL: func.func @roundeven_f64(
+// CHECK: arith.shli
+// CHECK: return
+// LLVM-LABEL: llvm.func @roundeven_f64(
+// LLVM: llvm.shl
+// LLVM: llvm.return
+func.func @roundeven_f64(%value: f64) -> f64 {
+  %result = math.roundeven %value : f64
+  return %result : f64
+}
+
+// VALUES-LABEL: func.func @even_tie(
+// VALUES: %[[EXPECTED:.*]] = arith.constant 4611686018427387904 : i64
+// VALUES-NEXT: return %[[EXPECTED]] : i64
+func.func @even_tie() -> i64 {
+  %value = arith.constant 2.5 : f64
+  %rounded = func.call @roundeven_f64(%value) : (f64) -> f64
+  %result = arith.bitcast %rounded : f64 to i64
+  return %result : i64
+}

>From 5998895972e0b7ad40d926a75765fa08afca8833 Mon Sep 17 00:00:00 2001
From: Sriram Shastry <sriramshastry at gmail.com>
Date: Fri, 9 Oct 2026 08:48:01 +0000
Subject: [PATCH 2/2] [mlir][math] Fix round expansion formatting

Signed-off-by: Sriram Shastry <sriramshastry at gmail.com>
---
 mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
index f0ec498b737432..c6d70b565b17f0 100644
--- a/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
+++ b/mlir/lib/Dialect/Math/Transforms/ExpandOps.cpp
@@ -519,7 +519,7 @@ static LogicalResult convertExp2fOp(math::Exp2Op op,
 }
 
 static LogicalResult convertRoundF64Op(math::RoundOp op,
-                                      PatternRewriter &rewriter) {
+                                       PatternRewriter &rewriter) {
   Location loc = op.getLoc();
   ImplicitLocOpBuilder b(loc, rewriter);
   Value operand = op.getOperand();
@@ -569,7 +569,7 @@ static LogicalResult convertRoundF64Op(math::RoundOp op,
   Value resultMagnitude =
       arith::SelectOp::create(b, isSmall, smallMagnitude, roundedMagnitude);
   Value isLarge = arith::CmpIOp::create(b, arith::CmpIPredicate::sge, exponent,
-                                      cFractionBits);
+                                        cFractionBits);
   resultMagnitude =
       arith::SelectOp::create(b, isLarge, magnitude, resultMagnitude);
   Value resultBits = arith::OrIOp::create(b, resultMagnitude, sign);



More information about the Mlir-commits mailing list