Skip to content

intrinsics: unify rint, roundeven, nearbyint in a single round_ties_even intrinsic #136543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,10 @@ fn codegen_float_intrinsic_call<'tcx>(
sym::ceilf64 => ("ceil", 1, fx.tcx.types.f64, types::F64),
sym::truncf32 => ("truncf", 1, fx.tcx.types.f32, types::F32),
sym::truncf64 => ("trunc", 1, fx.tcx.types.f64, types::F64),
sym::rintf32 => ("rintf", 1, fx.tcx.types.f32, types::F32),
sym::rintf64 => ("rint", 1, fx.tcx.types.f64, types::F64),
sym::round_ties_even_f32 => ("rintf", 1, fx.tcx.types.f32, types::F32),
sym::round_ties_even_f64 => ("rint", 1, fx.tcx.types.f64, types::F64),
sym::roundf32 => ("roundf", 1, fx.tcx.types.f32, types::F32),
sym::roundf64 => ("round", 1, fx.tcx.types.f64, types::F64),
sym::roundevenf32 => ("roundevenf", 1, fx.tcx.types.f32, types::F32),
sym::roundevenf64 => ("roundeven", 1, fx.tcx.types.f64, types::F64),
sym::nearbyintf32 => ("nearbyintf", 1, fx.tcx.types.f32, types::F32),
sym::nearbyintf64 => ("nearbyint", 1, fx.tcx.types.f64, types::F64),
sym::sinf32 => ("sinf", 1, fx.tcx.types.f32, types::F32),
sym::sinf64 => ("sin", 1, fx.tcx.types.f64, types::F64),
sym::cosf32 => ("cosf", 1, fx.tcx.types.f32, types::F32),
Expand Down Expand Up @@ -399,16 +395,18 @@ fn codegen_float_intrinsic_call<'tcx>(
| sym::ceilf64
| sym::truncf32
| sym::truncf64
| sym::nearbyintf32
| sym::nearbyintf64
| sym::round_ties_even_f32
| sym::round_ties_even_f64
| sym::sqrtf32
| sym::sqrtf64 => {
let val = match intrinsic {
sym::fabsf32 | sym::fabsf64 => fx.bcx.ins().fabs(args[0]),
sym::floorf32 | sym::floorf64 => fx.bcx.ins().floor(args[0]),
sym::ceilf32 | sym::ceilf64 => fx.bcx.ins().ceil(args[0]),
sym::truncf32 | sym::truncf64 => fx.bcx.ins().trunc(args[0]),
sym::nearbyintf32 | sym::nearbyintf64 => fx.bcx.ins().nearest(args[0]),
sym::round_ties_even_f32 | sym::round_ties_even_f64 => {
fx.bcx.ins().nearest(args[0])
}
Copy link
Member Author

@RalfJung RalfJung Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means the cranelift backend will actually change behavior with this PR -- the round_ties_even functions will lower to the "nearest" instruction rather than to rint libcalls. @bjorn3 is that okay?

I am also surprised to not see a case for round here (rounding half-way cases away from zero); is that the one rounding mode cranelit does not support natively?

Copy link
Contributor

@tgross35 tgross35 Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this ever lowers to nearbyint then we do not currently provide that in libm so there may be linking problems on targets without a system libm. I don't expect this to be a problem for cranelift though.

sym::sqrtf32 | sym::sqrtf64 => fx.bcx.ins().sqrt(args[0]),
_ => unreachable!(),
};
Expand Down
9 changes: 3 additions & 6 deletions compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,11 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
sym::ceilf64 => "ceil",
sym::truncf32 => "truncf",
sym::truncf64 => "trunc",
sym::rintf32 => "rintf",
sym::rintf64 => "rint",
sym::nearbyintf32 => "nearbyintf",
sym::nearbyintf64 => "nearbyint",
// We match the LLVM backend and lower this to `rint`.
sym::round_ties_even_f32 => "rintf",
sym::round_ties_even_f64 => "rint",
sym::roundf32 => "roundf",
sym::roundf64 => "round",
sym::roundevenf32 => "roundevenf",
sym::roundevenf64 => "roundeven",
sym::abort => "abort",
_ => return None,
};
Expand Down
22 changes: 8 additions & 14 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,14 @@ fn get_simple_intrinsic<'ll>(
sym::truncf64 => "llvm.trunc.f64",
sym::truncf128 => "llvm.trunc.f128",

sym::rintf16 => "llvm.rint.f16",
sym::rintf32 => "llvm.rint.f32",
sym::rintf64 => "llvm.rint.f64",
sym::rintf128 => "llvm.rint.f128",

sym::nearbyintf16 => "llvm.nearbyint.f16",
sym::nearbyintf32 => "llvm.nearbyint.f32",
sym::nearbyintf64 => "llvm.nearbyint.f64",
sym::nearbyintf128 => "llvm.nearbyint.f128",
// We could use any of `rint`, `nearbyint`, or `roundeven`
// for this -- they are all identical in semantics when
// assuming the default FP environment.
// `rint` is what we used for $forever.
sym::round_ties_even_f16 => "llvm.rint.f16",
sym::round_ties_even_f32 => "llvm.rint.f32",
sym::round_ties_even_f64 => "llvm.rint.f64",
sym::round_ties_even_f128 => "llvm.rint.f128",

sym::roundf16 => "llvm.round.f16",
sym::roundf32 => "llvm.round.f32",
Expand All @@ -143,11 +142,6 @@ fn get_simple_intrinsic<'ll>(

sym::ptr_mask => "llvm.ptrmask",

sym::roundevenf16 => "llvm.roundeven.f16",
sym::roundevenf32 => "llvm.roundeven.f32",
sym::roundevenf64 => "llvm.roundeven.f64",
sym::roundevenf128 => "llvm.roundeven.f128",

_ => return None,
};
Some(cx.get_intrinsic(llvm_name))
Expand Down
18 changes: 4 additions & 14 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,26 +398,16 @@ pub fn check_intrinsic_type(
sym::truncf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::truncf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),

sym::rintf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
sym::rintf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
sym::rintf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::rintf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),

sym::nearbyintf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
sym::nearbyintf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
sym::nearbyintf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::nearbyintf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),
sym::round_ties_even_f16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
sym::round_ties_even_f32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
sym::round_ties_even_f64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::round_ties_even_f128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),

sym::roundf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
sym::roundf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
sym::roundf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::roundf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),

sym::roundevenf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16),
sym::roundevenf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32),
sym::roundevenf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
sym::roundevenf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),

sym::volatile_load | sym::unaligned_volatile_load => {
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0))
}
Expand Down
16 changes: 4 additions & 12 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,10 +1337,6 @@ symbols! {
native_link_modifiers_whole_archive,
natvis_file,
ne,
nearbyintf128,
nearbyintf16,
nearbyintf32,
nearbyintf64,
needs_allocator,
needs_drop,
needs_panic_runtime,
Expand Down Expand Up @@ -1655,20 +1651,16 @@ symbols! {
return_position_impl_trait_in_trait,
return_type_notation,
rhs,
rintf128,
rintf16,
rintf32,
rintf64,
riscv_target_feature,
rlib,
ropi,
ropi_rwpi: "ropi-rwpi",
rotate_left,
rotate_right,
roundevenf128,
roundevenf16,
roundevenf32,
roundevenf64,
round_ties_even_f128,
round_ties_even_f16,
round_ties_even_f32,
round_ties_even_f64,
roundf128,
roundf16,
roundf32,
Expand Down
185 changes: 73 additions & 112 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2739,110 +2739,112 @@ pub unsafe fn truncf128(_x: f128) -> f128 {
unreachable!()
}

/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// May raise an inexact floating-point exception if the argument is not an integer.
/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions
/// cannot actually be utilized from Rust code.
/// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`.
/// Returns the nearest integer to an `f16`. Rounds half-way cases to the number with an even
/// least significant digit.
///
/// The stabilized version of this intrinsic is
/// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even)
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn rintf16(_x: f16) -> f16 {
#[cfg(not(bootstrap))]
pub unsafe fn round_ties_even_f16(_x: f16) -> f16 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the latest on safe vs. unsafe intrinsics? There shouldn't be any preconditions here so it could probably be updated while you're at it (not necessary ofc).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I guess I can mark this safe while I am at it, though it is strangely inconsistent with the other rounding intrinsics.

unreachable!()
}
/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// May raise an inexact floating-point exception if the argument is not an integer.
/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions
/// cannot actually be utilized from Rust code.
/// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`.

/// To be removed on next bootstrap bump.
#[cfg(bootstrap)]
pub unsafe fn round_ties_even_f16(x: f16) -> f16 {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
unsafe fn rintf16(_x: f16) -> f16 {
unreachable!()
}

// SAFETY: this intrinsic isn't actually unsafe
unsafe { rintf16(x) }
}

/// Returns the nearest integer to an `f32`. Rounds half-way cases to the number with an even
/// least significant digit.
///
/// The stabilized version of this intrinsic is
/// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even)
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn rintf32(_x: f32) -> f32 {
#[cfg(not(bootstrap))]
pub unsafe fn round_ties_even_f32(_x: f32) -> f32 {
unreachable!()
}
/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// May raise an inexact floating-point exception if the argument is not an integer.
/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions
/// cannot actually be utilized from Rust code.
/// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`.

/// To be removed on next bootstrap bump.
#[cfg(bootstrap)]
pub unsafe fn round_ties_even_f32(x: f32) -> f32 {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
unsafe fn rintf32(_x: f32) -> f32 {
unreachable!()
}

// SAFETY: this intrinsic isn't actually unsafe
unsafe { rintf32(x) }
}

/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number with an even
/// least significant digit.
///
/// The stabilized version of this intrinsic is
/// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even)
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn rintf64(_x: f64) -> f64 {
#[cfg(not(bootstrap))]
pub unsafe fn round_ties_even_f64(_x: f64) -> f64 {
unreachable!()
}
/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// May raise an inexact floating-point exception if the argument is not an integer.
/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions
/// cannot actually be utilized from Rust code.
/// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`.

/// To be removed on next bootstrap bump.
#[cfg(bootstrap)]
pub unsafe fn round_ties_even_f64(x: f64) -> f64 {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
unsafe fn rintf64(_x: f64) -> f64 {
unreachable!()
}

// SAFETY: this intrinsic isn't actually unsafe
unsafe { rintf64(x) }
}

/// Returns the nearest integer to an `f128`. Rounds half-way cases to the number with an even
/// least significant digit.
///
/// The stabilized version of this intrinsic is
/// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even)
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn rintf128(_x: f128) -> f128 {
#[cfg(not(bootstrap))]
pub unsafe fn round_ties_even_f128(_x: f128) -> f128 {
unreachable!()
}

/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn nearbyintf16(_x: f16) -> f16 {
unreachable!()
}
/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn nearbyintf32(_x: f32) -> f32 {
unreachable!()
}
/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn nearbyintf64(_x: f64) -> f64 {
unreachable!()
}
/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust,
/// so this rounds half-way cases to the number with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn nearbyintf128(_x: f128) -> f128 {
unreachable!()
/// To be removed on next bootstrap bump.
#[cfg(bootstrap)]
pub unsafe fn round_ties_even_f128(x: f128) -> f128 {
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
unsafe fn rintf128(_x: f128) -> f128 {
unreachable!()
}

// SAFETY: this intrinsic isn't actually unsafe
unsafe { rintf128(x) }
}

/// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero.
Expand Down Expand Up @@ -2886,47 +2888,6 @@ pub unsafe fn roundf128(_x: f128) -> f128 {
unreachable!()
}

/// Returns the nearest integer to an `f16`. Rounds half-way cases to the number
/// with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn roundevenf16(_x: f16) -> f16 {
unreachable!()
}
/// Returns the nearest integer to an `f32`. Rounds half-way cases to the number
/// with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn roundevenf32(_x: f32) -> f32 {
unreachable!()
}
/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number
/// with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn roundevenf64(_x: f64) -> f64 {
unreachable!()
}
/// Returns the nearest integer to an `f128`. Rounds half-way cases to the number
/// with an even least significant digit.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[rustc_nounwind]
pub unsafe fn roundevenf128(_x: f128) -> f128 {
unreachable!()
}

/// Float addition that allows optimizations based on algebraic rules.
/// May assume inputs are finite.
///
Expand Down
Loading
Loading