[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:39:24 PDT 2026


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

This adds an opt-in f64 `math.round` expansion. The `expand-round-f64` option
defaults to false, preserving existing defaults, f32 lowering and direct
MathToLLVM conversion. No additional SCF lowering step is required.
Supported cases: scalars, fixed/scalable vectors and ranked tensors. The existing
`ops` filter applies, including to Round generated by RoundEven.

Validation at `23d1cc7e11b25ff0fe27a6297f8d0cc3064f8787` (Release/assertions):
Rebuilt the affected source and generated pass header, then ran all 13 RUN
commands in the three affected test files; all passed. Default expansion and
direct LLVM conversion produced the same output as the baseline. With f64
expansion enabled, sampled Round and RoundEven checks found no mismatches for
either generic or Alder Lake code generation. The tests cover rounding
boundaries, special values, masked poison results and lowering without branches.

Reused code-identical synthetic kernel timings compare with the same pipeline
using direct MathToLLVM rounding intrinsics, without expansion. Median paired
time reductions, uniform/mixed inputs, 45 pairs per cell:

| Kernel | Generic U/M | Alder Lake U/M |
| --- | ---: | ---: |
| Scalar f64 | +7.50% / +44.65% | -499.12% / -503.65% |
| v2f64 | +16.71% / +47.38% | -245.93% / -246.70% |

Enabled expansion has target-dependent performance: measured generic-target
kernels improve, while Alder Lake kernels take approximately $6\times$ the
scalar time and $3.5\times$ the vector time compared with direct LLVM lowering.
Direct LLVM lowering remains preferred where efficient native support exists.

Negative means slower. Measurements use O2 native x86 kernels on one Core Ultra
host under WSL; scheduling/frequency were uncontrolled. No old f64 expansion
baseline exists. Prefer direct native LLVM; no universal or AI speedup is claimed.
No downstream consumer requiring this option has been established.

Completed checks cover sampled numerical results, boundary and special values,
poison masking and branch-free lowering. Full-project CI, exhaustive validation,
native ARM and tensor/scalable-vector execution validation remain outstanding.
Strict floating-point flags, traps and ordering are outside this proposal's scope.

This draft requests feedback on two design questions:

1. Is this f64 fallback expansion useful upstream?
2. Is the proposed default-false expand-round-f64 pass option the appropriate interface?

>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] [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
+}



More information about the Mlir-commits mailing list