[Mlir-commits] [mlir] [mlir][math] Add opt-in f64 round expansion (PR #230408)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Fri Oct 9 03:08:18 PDT 2026
https://github.com/sriramshastry updated https://github.com/llvm/llvm-project/pull/230408
>From 385dc8b6d4a034eab21486e6386eb239eb9d511c 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.
(1) 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.
(2) 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.
U/M means uniform/mixed inputs.
| Kernel | Generic x86 U/M | CPU-specific x86 U/M |
| ---------- | ----------------: | -------------------: |
| 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.
(3) 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 f64 fallback capability useful upstream?
2. Is the default-false expand-round-f64 pass option the appropriate
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