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

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Oct 9 02:05:33 PDT 2026


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

>From 1cabc80e0f19d9948a219de4225c2736888b184a 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

This adds an optional f64 expansion for `math.round`. The `expand-round-f64`
option is off by default, so existing defaults, f32 lowering and direct
MathToLLVM conversion stay unchanged.

It supports scalars, fixed and scalable vectors, and ranked tensors. No extra
SCF lowering step is needed. The existing `ops` filter still applies, including
to Round operations generated by RoundEven.

### Testing

Local testing used a Release build with assertions enabled. The affected source
and generated pass header were rebuilt. All 13 RUN commands in the three
affected test files passed.

Default expansion and direct LLVM conversion matched the baseline. With the
option enabled, sampled Round and RoundEven checks found no mismatches in either
tested x86 configuration. Checks covered rounding boundaries, special values,
masked poison results and lowering without branches.

### Performance

Earlier synthetic kernel timings were reused because the enabled f64 code is
unchanged from the measured version. The baseline uses the same pipeline with
direct MathToLLVM rounding intrinsics and no expansion.

Both x86 configurations ran on the same Core Ultra host under WSL. The
CPU-specific configuration used `-mcpu=alderlake`; it was not run on a separate
Alder Lake machine.

Median paired time reductions, with 45 pairs per case:

| Kernel | Generic x86, uniform / mixed | CPU-specific x86, uniform / mixed |
| --- | ---: | ---: |
| Scalar f64 | +7.50% / +44.65% | -499.12% / -503.65% |
| v2f64 | +16.71% / +47.38% | -245.93% / -246.70% |

Positive means faster; negative means slower. The generic-target kernels
improved. The CPU-specific kernels took about six times the scalar time and
three and a half times the vector time.

The expansion is therefore kept optional. Direct LLVM lowering remains
preferred where efficient native support exists.

These measurements used O2 native x86 kernels. Scheduling and CPU frequency
were not controlled. There is no previous f64 expansion baseline, and these
results do not show an application-level or general speedup. No downstream user
needing this option has been identified yet.

### Open Questions

The local checks are not exhaustive and do not establish full-project CI
success. Native ARM execution and tensor/scalable-vector execution remain
untested here. Strict floating-point exception flags, traps and ordering are
outside this change.

This draft asks for feedback on two points:

1. Is this optional f64 fallback useful upstream?
2. Is the default-off `expand-round-f64` pass option the right interface?

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..c6d70b565b17f0 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