Skip to content

Commit

Permalink
Make some float methods unstable const fn
Browse files Browse the repository at this point in the history
Some float methods are now `const fn` under the `const_float_methods` feature gate (except `f16` and `f128` versions, which I placed under `f16` and `f128` feature gates respectively).

In order to support `min` / `max`, the implementation of some intrinsics had to be moved from Miri to rustc_const_eval.
  • Loading branch information
eduardosm committed Sep 19, 2024
1 parent b0af276 commit 4c6f58d
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 148 deletions.
56 changes: 56 additions & 0 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use std::assert_matches::assert_matches;

use rustc_apfloat::Float;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
Expand Down Expand Up @@ -109,6 +110,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let instance_args = instance.args;
let intrinsic_name = self.tcx.item_name(instance.def_id());

// Performs appropriate non-deterministic adjustments of NaN results.
fn adjust_nan<
'tcx,
M: Machine<'tcx>,
F1: Float + rustc_apfloat::FloatConvert<F2>,
F2: Float,
>(
this: &InterpCx<'tcx, M>,
f: F2,
inputs: &[F1],
) -> F2 {
if f.is_nan() { M::generate_nan(this, inputs) } else { f }
}

match intrinsic_name {
sym::caller_location => {
let span = self.find_closest_untracked_caller_location();
Expand Down Expand Up @@ -440,6 +455,47 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
}

sym::minnumf16 | sym::maxnumf16 => {
let a = self.read_scalar(&args[0])?.to_f16()?;
let b = self.read_scalar(&args[1])?.to_f16()?;
let res = match intrinsic_name {
sym::minnumf16 => a.min(b),
sym::maxnumf16 => a.max(b),
_ => bug!(),
};
self.write_scalar(adjust_nan(self, res, &[a, b]), dest)?;
}
sym::minnumf32 | sym::maxnumf32 => {
let a = self.read_scalar(&args[0])?.to_f32()?;
let b = self.read_scalar(&args[1])?.to_f32()?;
let res = match intrinsic_name {
sym::minnumf32 => a.min(b),
sym::maxnumf32 => a.max(b),
_ => bug!(),
};
self.write_scalar(adjust_nan(self, res, &[a, b]), dest)?;
}
sym::minnumf64 | sym::maxnumf64 => {
let a = self.read_scalar(&args[0])?.to_f64()?;
let b = self.read_scalar(&args[1])?.to_f64()?;
let res = match intrinsic_name {
sym::minnumf64 => a.min(b),
sym::maxnumf64 => a.max(b),
_ => bug!(),
};
self.write_scalar(adjust_nan(self, res, &[a, b]), dest)?;
}
sym::minnumf128 | sym::maxnumf128 => {
let a = self.read_scalar(&args[0])?.to_f128()?;
let b = self.read_scalar(&args[1])?.to_f128()?;
let res = match intrinsic_name {
sym::minnumf128 => a.min(b),
sym::maxnumf128 => a.max(b),
_ => bug!(),
};
self.write_scalar(adjust_nan(self, res, &[a, b]), dest)?;
}

// Unsupported intrinsic: skip the return_to_block below.
_ => return Ok(false),
}
Expand Down
234 changes: 136 additions & 98 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1821,104 +1821,6 @@ extern "rust-intrinsic" {
#[rustc_nounwind]
pub fn fabsf128(x: f128) -> f128;

/// Returns the minimum of two `f16` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f16::min`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn minnumf16(x: f16, y: f16) -> f16;
/// Returns the minimum of two `f32` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f32::min`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn minnumf32(x: f32, y: f32) -> f32;
/// Returns the minimum of two `f64` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f64::min`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn minnumf64(x: f64, y: f64) -> f64;
/// Returns the minimum of two `f128` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f128::min`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn minnumf128(x: f128, y: f128) -> f128;

/// Returns the maximum of two `f16` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f16::max`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn maxnumf16(x: f16, y: f16) -> f16;
/// Returns the maximum of two `f32` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f32::max`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn maxnumf32(x: f32, y: f32) -> f32;
/// Returns the maximum of two `f64` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f64::max`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn maxnumf64(x: f64, y: f64) -> f64;
/// Returns the maximum of two `f128` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f128::max`]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn maxnumf128(x: f128, y: f128) -> f128;

/// Copies the sign from `y` to `x` for `f16` values.
///
/// The stabilized version of this intrinsic is
Expand Down Expand Up @@ -3493,6 +3395,142 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
}
}

/// Returns the minimum of two `f16` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f16::min`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "f16", issue = "116909")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn minnumf16(_x: f16, _y: f16) -> f16 {
unimplemented!();
}

/// Returns the minimum of two `f32` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f32::min`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "const_float_methods", issue = "none")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn minnumf32(_x: f32, _y: f32) -> f32 {
unimplemented!();
}

/// Returns the minimum of two `f64` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f64::min`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "const_float_methods", issue = "none")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn minnumf64(_x: f64, _y: f64) -> f64 {
unimplemented!();
}

/// Returns the minimum of two `f128` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f128::min`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "f128", issue = "116909")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn minnumf128(_x: f128, _y: f128) -> f128 {
unimplemented!();
}

/// Returns the maximum of two `f16` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f16::max`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "f16", issue = "116909")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn maxnumf16(_x: f16, _y: f16) -> f16 {
unimplemented!();
}

/// Returns the maximum of two `f32` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f32::max`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "const_float_methods", issue = "none")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn maxnumf32(_x: f32, _y: f32) -> f32 {
unimplemented!();
}

/// Returns the maximum of two `f64` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f64::max`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "const_float_methods", issue = "none")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn maxnumf64(_x: f64, _y: f64) -> f64 {
unimplemented!();
}

/// Returns the maximum of two `f128` values.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is
/// [`f128::max`]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "f128", issue = "116909")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn maxnumf128(_x: f128, _y: f128) -> f128 {
unimplemented!();
}

/// Inform Miri that a given pointer definitely has a certain alignment.
#[cfg(miri)]
pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize) {
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
#![feature(const_eval_select)]
#![feature(const_exact_div)]
#![feature(const_float_classify)]
#![feature(const_float_methods)]
#![feature(const_fmt_arguments_new)]
#![feature(const_hash)]
#![feature(const_heap)]
Expand Down
Loading

0 comments on commit 4c6f58d

Please sign in to comment.