mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
rename min/maxnum intrinsics to min/maximum_number and fix their LLVM lowering
This commit is contained in:
@@ -1266,7 +1266,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
|
||||
sym::minnumf16 => {
|
||||
sym::minimum_number_nsz_f16 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1275,7 +1275,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::minnumf32 => {
|
||||
sym::minimum_number_nsz_f32 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1284,7 +1284,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::minnumf64 => {
|
||||
sym::minimum_number_nsz_f64 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1293,7 +1293,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::minnumf128 => {
|
||||
sym::minimum_number_nsz_f128 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1302,7 +1302,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f128));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::maxnumf16 => {
|
||||
sym::maximum_number_nsz_f16 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1311,7 +1311,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f16));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::maxnumf32 => {
|
||||
sym::maximum_number_nsz_f32 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1320,7 +1320,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::maxnumf64 => {
|
||||
sym::maximum_number_nsz_f64 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
@@ -1329,7 +1329,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::maxnumf128 => {
|
||||
sym::maximum_number_nsz_f128 => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
let a = a.load_scalar(fx);
|
||||
let b = b.load_scalar(fx);
|
||||
|
||||
@@ -498,10 +498,10 @@ fn codegen_ptr_binop<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
// In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
|
||||
// For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
|
||||
// and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
|
||||
// a float against itself. Only in case of NaN is it not equal to itself.
|
||||
// In Rust floating point min and max don't propagate NaN (not even SNaN). In Cranelift they do
|
||||
// however. For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for
|
||||
// `minnumf*` and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by
|
||||
// comparing a float against itself. Only in case of NaN is it not equal to itself.
|
||||
pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
|
||||
// FIXME(bytecodealliance/wasmtime#8312): Replace with Cranelift `fcmp` once
|
||||
// `f16`/`f128` backend lowerings have been added to Cranelift.
|
||||
|
||||
@@ -72,8 +72,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
||||
sym::fmuladdf64 => "fma", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64
|
||||
sym::fabsf32 => "fabsf",
|
||||
sym::fabsf64 => "fabs",
|
||||
sym::minnumf32 => "fminf",
|
||||
sym::minnumf64 => "fmin",
|
||||
sym::minimumf32 => "fminimumf",
|
||||
sym::minimumf64 => "fminimum",
|
||||
sym::minimumf128 => {
|
||||
@@ -92,8 +90,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
|
||||
false,
|
||||
));
|
||||
}
|
||||
sym::maxnumf32 => "fmaxf",
|
||||
sym::maxnumf64 => "fmax",
|
||||
sym::maximumf32 => "fmaximumf",
|
||||
sym::maximumf64 => "fmaximum",
|
||||
sym::maximumf128 => {
|
||||
@@ -236,8 +232,6 @@ fn get_simple_function_f128_2args<'gcc, 'tcx>(
|
||||
|
||||
let f128_type = cx.type_f128();
|
||||
let func_name = match name {
|
||||
sym::maxnumf128 => "fmaxf128",
|
||||
sym::minnumf128 => "fminf128",
|
||||
sym::copysignf128 => "copysignf128",
|
||||
_ => return None,
|
||||
};
|
||||
@@ -266,8 +260,6 @@ fn f16_builtin<'gcc, 'tcx>(
|
||||
sym::fabsf16 => "fabsf",
|
||||
sym::floorf16 => "__builtin_floorf",
|
||||
sym::fmaf16 => "fmaf",
|
||||
sym::maxnumf16 => "__builtin_fmaxf",
|
||||
sym::minnumf16 => "__builtin_fminf",
|
||||
sym::powf16 => "__builtin_powf",
|
||||
sym::powif16 => {
|
||||
let func = cx.context.get_builtin_function("__builtin_powif");
|
||||
@@ -333,8 +325,6 @@ fn codegen_intrinsic_call(
|
||||
| sym::fabsf16
|
||||
| sym::floorf16
|
||||
| sym::fmaf16
|
||||
| sym::maxnumf16
|
||||
| sym::minnumf16
|
||||
| sym::powf16
|
||||
| sym::powif16
|
||||
| sym::roundf16
|
||||
|
||||
@@ -112,11 +112,6 @@ fn call_simple_intrinsic<'ll, 'tcx>(
|
||||
sym::fabsf64 => ("llvm.fabs", &[bx.type_f64()]),
|
||||
sym::fabsf128 => ("llvm.fabs", &[bx.type_f128()]),
|
||||
|
||||
sym::minnumf16 => ("llvm.minnum", &[bx.type_f16()]),
|
||||
sym::minnumf32 => ("llvm.minnum", &[bx.type_f32()]),
|
||||
sym::minnumf64 => ("llvm.minnum", &[bx.type_f64()]),
|
||||
sym::minnumf128 => ("llvm.minnum", &[bx.type_f128()]),
|
||||
|
||||
// FIXME: LLVM currently mis-compile those intrinsics, re-enable them
|
||||
// when llvm/llvm-project#{139380,139381,140445} are fixed.
|
||||
//sym::minimumf16 => ("llvm.minimum", &[bx.type_f16()]),
|
||||
@@ -124,11 +119,6 @@ fn call_simple_intrinsic<'ll, 'tcx>(
|
||||
//sym::minimumf64 => ("llvm.minimum", &[bx.type_f64()]),
|
||||
//sym::minimumf128 => ("llvm.minimum", &[cx.type_f128()]),
|
||||
//
|
||||
sym::maxnumf16 => ("llvm.maxnum", &[bx.type_f16()]),
|
||||
sym::maxnumf32 => ("llvm.maxnum", &[bx.type_f32()]),
|
||||
sym::maxnumf64 => ("llvm.maxnum", &[bx.type_f64()]),
|
||||
sym::maxnumf128 => ("llvm.maxnum", &[bx.type_f128()]),
|
||||
|
||||
// FIXME: LLVM currently mis-compile those intrinsics, re-enable them
|
||||
// when llvm/llvm-project#{139380,139381,140445} are fixed.
|
||||
//sym::maximumf16 => ("llvm.maximum", &[bx.type_f16()]),
|
||||
@@ -195,6 +185,32 @@ fn codegen_intrinsic_call(
|
||||
let simple = call_simple_intrinsic(self, name, args);
|
||||
let llval = match name {
|
||||
_ if simple.is_some() => simple.unwrap(),
|
||||
sym::minimum_number_nsz_f16
|
||||
| sym::minimum_number_nsz_f32
|
||||
| sym::minimum_number_nsz_f64
|
||||
| sym::minimum_number_nsz_f128
|
||||
| sym::maximum_number_nsz_f16
|
||||
| sym::maximum_number_nsz_f32
|
||||
| sym::maximum_number_nsz_f64
|
||||
| sym::maximum_number_nsz_f128
|
||||
// Need at least LLVM 22 for `min/maximumnum` to not crash LLVM.
|
||||
if crate::llvm_util::get_version() >= (22, 0, 0) =>
|
||||
{
|
||||
let intrinsic_name = if name.as_str().starts_with("min") {
|
||||
"llvm.minimumnum"
|
||||
} else {
|
||||
"llvm.maximumnum"
|
||||
};
|
||||
let call = self.call_intrinsic(
|
||||
intrinsic_name,
|
||||
&[args[0].layout.immediate_llvm_type(self.cx)],
|
||||
&[args[0].immediate(), args[1].immediate()],
|
||||
);
|
||||
// `nsz` on minimumnum/maximumnum is special: its only effect is to make
|
||||
// signed-zero ordering non-deterministic.
|
||||
unsafe { llvm::LLVMRustSetNoSignedZeros(call) };
|
||||
call
|
||||
}
|
||||
sym::ptr_mask => {
|
||||
let ptr = args[0].immediate();
|
||||
self.call_intrinsic(
|
||||
|
||||
@@ -2045,6 +2045,7 @@ pub(crate) fn LLVMRustAddCallSiteAttributes<'a>(
|
||||
pub(crate) fn LLVMRustSetFastMath(Instr: &Value);
|
||||
pub(crate) fn LLVMRustSetAlgebraicMath(Instr: &Value);
|
||||
pub(crate) fn LLVMRustSetAllowReassoc(Instr: &Value);
|
||||
pub(crate) fn LLVMRustSetNoSignedZeros(Instr: &Value);
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub(crate) fn LLVMRustBuildMemCpy<'a>(
|
||||
|
||||
@@ -40,20 +40,20 @@ pub(crate) enum MinMax {
|
||||
/// In particular, `-0.0` is considered smaller than `+0.0` and
|
||||
/// if either input is NaN, the result is NaN.
|
||||
Minimum,
|
||||
/// The IEEE-2008 `minNum` operation with the SNaN handling of the
|
||||
/// IEEE-2019 `minimumNumber` operation - see `f32::min` etc.
|
||||
/// The IEEE-2019 `minimumNumber` operation but with non-deterministic signed zero handling
|
||||
/// (like in IEEE-2008 `minNum`) - see `f32::min` etc.
|
||||
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
|
||||
/// and if one argument is NaN (quiet or signaling), the other one is returned.
|
||||
MinimumNumber,
|
||||
MinimumNumberNsz,
|
||||
/// The IEEE-2019 `maximum` operation - see `f32::maximum` etc.
|
||||
/// In particular, `-0.0` is considered smaller than `+0.0` and
|
||||
/// if either input is NaN, the result is NaN.
|
||||
Maximum,
|
||||
/// The IEEE-2008 `maxNum` operation with the SNaN handling of the
|
||||
/// IEEE-2019 `maximumNumber` operation - see `f32::max` etc.
|
||||
/// The IEEE-2019 `maximumNumber` operation but with non-deterministic signed zero handling
|
||||
/// (like in IEEE-2008 `maxNum`) - see `f32::max` etc.
|
||||
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
|
||||
/// and if one argument is NaN (quiet or signaling), the other one is returned.
|
||||
MaximumNumber,
|
||||
MaximumNumberNsz,
|
||||
}
|
||||
|
||||
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
|
||||
@@ -526,17 +526,17 @@ pub fn eval_intrinsic(
|
||||
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
|
||||
}
|
||||
|
||||
sym::minnumf16 => {
|
||||
self.float_minmax_intrinsic::<Half>(args, MinMax::MinimumNumber, dest)?
|
||||
sym::minimum_number_nsz_f16 => {
|
||||
self.float_minmax_intrinsic::<Half>(args, MinMax::MinimumNumberNsz, dest)?
|
||||
}
|
||||
sym::minnumf32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::MinimumNumber, dest)?
|
||||
sym::minimum_number_nsz_f32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::MinimumNumberNsz, dest)?
|
||||
}
|
||||
sym::minnumf64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::MinimumNumber, dest)?
|
||||
sym::minimum_number_nsz_f64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::MinimumNumberNsz, dest)?
|
||||
}
|
||||
sym::minnumf128 => {
|
||||
self.float_minmax_intrinsic::<Quad>(args, MinMax::MinimumNumber, dest)?
|
||||
sym::minimum_number_nsz_f128 => {
|
||||
self.float_minmax_intrinsic::<Quad>(args, MinMax::MinimumNumberNsz, dest)?
|
||||
}
|
||||
|
||||
sym::minimumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Minimum, dest)?,
|
||||
@@ -548,17 +548,17 @@ pub fn eval_intrinsic(
|
||||
}
|
||||
sym::minimumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Minimum, dest)?,
|
||||
|
||||
sym::maxnumf16 => {
|
||||
self.float_minmax_intrinsic::<Half>(args, MinMax::MaximumNumber, dest)?
|
||||
sym::maximum_number_nsz_f16 => {
|
||||
self.float_minmax_intrinsic::<Half>(args, MinMax::MaximumNumberNsz, dest)?
|
||||
}
|
||||
sym::maxnumf32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::MaximumNumber, dest)?
|
||||
sym::maximum_number_nsz_f32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::MaximumNumberNsz, dest)?
|
||||
}
|
||||
sym::maxnumf64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::MaximumNumber, dest)?
|
||||
sym::maximum_number_nsz_f64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::MaximumNumberNsz, dest)?
|
||||
}
|
||||
sym::maxnumf128 => {
|
||||
self.float_minmax_intrinsic::<Quad>(args, MinMax::MaximumNumber, dest)?
|
||||
sym::maximum_number_nsz_f128 => {
|
||||
self.float_minmax_intrinsic::<Quad>(args, MinMax::MaximumNumberNsz, dest)?
|
||||
}
|
||||
|
||||
sym::maximumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Maximum, dest)?,
|
||||
@@ -1031,16 +1031,16 @@ fn float_minmax<F>(
|
||||
{
|
||||
let a: F = a.to_float()?;
|
||||
let b: F = b.to_float()?;
|
||||
let res = if matches!(op, MinMax::MinimumNumber | MinMax::MaximumNumber) && a == b {
|
||||
let res = if matches!(op, MinMax::MinimumNumberNsz | MinMax::MaximumNumberNsz) && a == b {
|
||||
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
|
||||
// Let the machine decide which one to return.
|
||||
M::equal_float_min_max(self, a, b)
|
||||
} else {
|
||||
let result = match op {
|
||||
MinMax::Minimum => a.minimum(b),
|
||||
MinMax::MinimumNumber => a.min(b),
|
||||
MinMax::MinimumNumberNsz => a.min(b),
|
||||
MinMax::Maximum => a.maximum(b),
|
||||
MinMax::MaximumNumber => a.max(b),
|
||||
MinMax::MaximumNumberNsz => a.max(b),
|
||||
};
|
||||
self.adjust_nan(result, &[a, b])
|
||||
};
|
||||
|
||||
@@ -211,8 +211,8 @@ enum Op {
|
||||
sym::simd_le => Op::MirOp(BinOp::Le),
|
||||
sym::simd_gt => Op::MirOp(BinOp::Gt),
|
||||
sym::simd_ge => Op::MirOp(BinOp::Ge),
|
||||
sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumber),
|
||||
sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumber),
|
||||
sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumberNsz),
|
||||
sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumberNsz),
|
||||
sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
|
||||
sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
|
||||
sym::simd_arith_offset => Op::WrappingOffset,
|
||||
@@ -304,8 +304,8 @@ enum Op {
|
||||
sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
|
||||
sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
|
||||
sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
|
||||
sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumber),
|
||||
sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumber),
|
||||
sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumberNsz),
|
||||
sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumberNsz),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -329,8 +329,8 @@ enum Op {
|
||||
} else {
|
||||
// Just boring integers, no NaNs to worry about.
|
||||
let mirop = match mmop {
|
||||
MinMax::MinimumNumber | MinMax::Minimum => BinOp::Le,
|
||||
MinMax::MaximumNumber | MinMax::Maximum => BinOp::Ge,
|
||||
MinMax::MinimumNumberNsz | MinMax::Minimum => BinOp::Le,
|
||||
MinMax::MaximumNumberNsz | MinMax::Maximum => BinOp::Ge,
|
||||
};
|
||||
if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
|
||||
res
|
||||
|
||||
@@ -147,22 +147,22 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
|
||||
| sym::logf32
|
||||
| sym::logf64
|
||||
| sym::logf128
|
||||
| sym::maximum_number_nsz_f16
|
||||
| sym::maximum_number_nsz_f32
|
||||
| sym::maximum_number_nsz_f64
|
||||
| sym::maximum_number_nsz_f128
|
||||
| sym::maximumf16
|
||||
| sym::maximumf32
|
||||
| sym::maximumf64
|
||||
| sym::maximumf128
|
||||
| sym::maxnumf16
|
||||
| sym::maxnumf32
|
||||
| sym::maxnumf64
|
||||
| sym::maxnumf128
|
||||
| sym::minimum_number_nsz_f16
|
||||
| sym::minimum_number_nsz_f32
|
||||
| sym::minimum_number_nsz_f64
|
||||
| sym::minimum_number_nsz_f128
|
||||
| sym::minimumf16
|
||||
| sym::minimumf32
|
||||
| sym::minimumf64
|
||||
| sym::minimumf128
|
||||
| sym::minnumf16
|
||||
| sym::minnumf32
|
||||
| sym::minnumf64
|
||||
| sym::minnumf128
|
||||
| sym::mul_with_overflow
|
||||
| sym::needs_drop
|
||||
| sym::offload
|
||||
@@ -468,20 +468,24 @@ pub(crate) fn check_intrinsic_type(
|
||||
sym::fabsf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64),
|
||||
sym::fabsf128 => (0, 0, vec![tcx.types.f128], tcx.types.f128),
|
||||
|
||||
sym::minnumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::minnumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
sym::minnumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
|
||||
sym::minnumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128),
|
||||
sym::minimum_number_nsz_f16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::minimum_number_nsz_f32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
sym::minimum_number_nsz_f64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
|
||||
sym::minimum_number_nsz_f128 => {
|
||||
(0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128)
|
||||
}
|
||||
|
||||
sym::minimumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::minimumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
sym::minimumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
|
||||
sym::minimumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128),
|
||||
|
||||
sym::maxnumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::maxnumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
sym::maxnumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
|
||||
sym::maxnumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128),
|
||||
sym::maximum_number_nsz_f16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::maximum_number_nsz_f32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
sym::maximum_number_nsz_f64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64),
|
||||
sym::maximum_number_nsz_f128 => {
|
||||
(0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128)
|
||||
}
|
||||
|
||||
sym::maximumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16),
|
||||
sym::maximumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32),
|
||||
|
||||
@@ -712,6 +712,13 @@ extern "C" void LLVMRustSetAllowReassoc(LLVMValueRef V) {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the NSZ flag on the given instruction.
|
||||
extern "C" void LLVMRustSetNoSignedZeros(LLVMValueRef V) {
|
||||
if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
|
||||
I->setHasNoSignedZeros(true);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" uint64_t LLVMRustGetArrayNumElements(LLVMTypeRef Ty) {
|
||||
return unwrap(Ty)->getArrayNumElements();
|
||||
}
|
||||
|
||||
@@ -1209,14 +1209,14 @@
|
||||
masked,
|
||||
match_beginning_vert,
|
||||
match_default_bindings,
|
||||
maximum_number_nsz_f16,
|
||||
maximum_number_nsz_f32,
|
||||
maximum_number_nsz_f64,
|
||||
maximum_number_nsz_f128,
|
||||
maximumf16,
|
||||
maximumf32,
|
||||
maximumf64,
|
||||
maximumf128,
|
||||
maxnumf16,
|
||||
maxnumf32,
|
||||
maxnumf64,
|
||||
maxnumf128,
|
||||
may_dangle,
|
||||
may_unwind,
|
||||
maybe_dangling,
|
||||
@@ -1247,14 +1247,14 @@
|
||||
min_generic_const_args,
|
||||
min_specialization,
|
||||
min_type_alias_impl_trait,
|
||||
minimum_number_nsz_f16,
|
||||
minimum_number_nsz_f32,
|
||||
minimum_number_nsz_f64,
|
||||
minimum_number_nsz_f128,
|
||||
minimumf16,
|
||||
minimumf32,
|
||||
minimumf64,
|
||||
minimumf128,
|
||||
minnumf16,
|
||||
minnumf32,
|
||||
minnumf64,
|
||||
minnumf128,
|
||||
mips,
|
||||
mips32r6,
|
||||
mips64,
|
||||
|
||||
@@ -2987,6 +2987,8 @@ pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M)
|
||||
|
||||
/// Returns the minimum of two `f16` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 minimumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -2999,10 +3001,19 @@ pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M)
|
||||
/// The stabilized version of this intrinsic is [`f16::min`].
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn minnumf16(x: f16, y: f16) -> f16;
|
||||
pub const fn minimum_number_nsz_f16(x: f16, y: f16) -> f16 {
|
||||
if x.is_nan() || y <= x {
|
||||
y
|
||||
} else {
|
||||
// Either y > x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum of two `f32` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 minimumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3016,10 +3027,19 @@ pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M)
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn minnumf32(x: f32, y: f32) -> f32;
|
||||
pub const fn minimum_number_nsz_f32(x: f32, y: f32) -> f32 {
|
||||
if x.is_nan() || y <= x {
|
||||
y
|
||||
} else {
|
||||
// Either y > x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum of two `f64` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 minimumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3033,10 +3053,19 @@ pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M)
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn minnumf64(x: f64, y: f64) -> f64;
|
||||
pub const fn minimum_number_nsz_f64(x: f64, y: f64) -> f64 {
|
||||
if x.is_nan() || y <= x {
|
||||
y
|
||||
} else {
|
||||
// Either y > x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum of two `f128` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 minimumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3049,7 +3078,14 @@ pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M)
|
||||
/// The stabilized version of this intrinsic is [`f128::min`].
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn minnumf128(x: f128, y: f128) -> f128;
|
||||
pub const fn minimum_number_nsz_f128(x: f128, y: f128) -> f128 {
|
||||
if x.is_nan() || y <= x {
|
||||
y
|
||||
} else {
|
||||
// Either y > x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum of two `f16` values, propagating NaN.
|
||||
///
|
||||
@@ -3153,6 +3189,8 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 {
|
||||
|
||||
/// Returns the maximum of two `f16` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 maximumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3165,10 +3203,19 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 {
|
||||
/// The stabilized version of this intrinsic is [`f16::max`].
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn maxnumf16(x: f16, y: f16) -> f16;
|
||||
pub const fn maximum_number_nsz_f16(x: f16, y: f16) -> f16 {
|
||||
if x.is_nan() || y >= x {
|
||||
y
|
||||
} else {
|
||||
// Either y < x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum of two `f32` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 maximumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3182,10 +3229,19 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 {
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn maxnumf32(x: f32, y: f32) -> f32;
|
||||
pub const fn maximum_number_nsz_f32(x: f32, y: f32) -> f32 {
|
||||
if x.is_nan() || y >= x {
|
||||
y
|
||||
} else {
|
||||
// Either y < x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum of two `f64` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 maximumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3199,10 +3255,19 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 {
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn maxnumf64(x: f64, y: f64) -> f64;
|
||||
pub const fn maximum_number_nsz_f64(x: f64, y: f64) -> f64 {
|
||||
if x.is_nan() || y >= x {
|
||||
y
|
||||
} else {
|
||||
// Either y < x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum of two `f128` values, ignoring NaN.
|
||||
///
|
||||
/// This behaves like IEEE 754-2019 maximumNumber, *except* that it does not order signed
|
||||
/// zeros deterministically. In particular:
|
||||
/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If
|
||||
/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0`
|
||||
/// and `-0.0`), either input may be returned non-deterministically.
|
||||
@@ -3215,7 +3280,14 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 {
|
||||
/// The stabilized version of this intrinsic is [`f128::max`].
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn maxnumf128(x: f128, y: f128) -> f128;
|
||||
pub const fn maximum_number_nsz_f128(x: f128, y: f128) -> f128 {
|
||||
if x.is_nan() || y >= x {
|
||||
y
|
||||
} else {
|
||||
// Either y < x or y is a NaN.
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum of two `f16` values, propagating NaN.
|
||||
///
|
||||
|
||||
@@ -790,7 +790,7 @@ pub const fn to_radians(self) -> f128 {
|
||||
#[rustc_const_unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the comparison, without modifying either input"]
|
||||
pub const fn max(self, other: f128) -> f128 {
|
||||
intrinsics::maxnumf128(self, other)
|
||||
intrinsics::maximum_number_nsz_f128(self, other)
|
||||
}
|
||||
|
||||
/// Returns the minimum of the two numbers, ignoring NaN.
|
||||
@@ -821,7 +821,7 @@ pub const fn max(self, other: f128) -> f128 {
|
||||
#[rustc_const_unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the comparison, without modifying either input"]
|
||||
pub const fn min(self, other: f128) -> f128 {
|
||||
intrinsics::minnumf128(self, other)
|
||||
intrinsics::minimum_number_nsz_f128(self, other)
|
||||
}
|
||||
|
||||
/// Returns the maximum of the two numbers, propagating NaN.
|
||||
|
||||
@@ -784,7 +784,7 @@ pub const fn to_radians(self) -> f16 {
|
||||
#[rustc_const_unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the comparison, without modifying either input"]
|
||||
pub const fn max(self, other: f16) -> f16 {
|
||||
intrinsics::maxnumf16(self, other)
|
||||
intrinsics::maximum_number_nsz_f16(self, other)
|
||||
}
|
||||
|
||||
/// Returns the minimum of the two numbers, ignoring NaN.
|
||||
@@ -815,7 +815,7 @@ pub const fn max(self, other: f16) -> f16 {
|
||||
#[rustc_const_unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the comparison, without modifying either input"]
|
||||
pub const fn min(self, other: f16) -> f16 {
|
||||
intrinsics::minnumf16(self, other)
|
||||
intrinsics::minimum_number_nsz_f16(self, other)
|
||||
}
|
||||
|
||||
/// Returns the maximum of the two numbers, propagating NaN.
|
||||
|
||||
@@ -990,7 +990,7 @@ pub const fn to_radians(self) -> f32 {
|
||||
#[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")]
|
||||
#[inline]
|
||||
pub const fn max(self, other: f32) -> f32 {
|
||||
intrinsics::maxnumf32(self, other)
|
||||
intrinsics::maximum_number_nsz_f32(self, other)
|
||||
}
|
||||
|
||||
/// Returns the minimum of the two numbers, ignoring NaN.
|
||||
@@ -1017,7 +1017,7 @@ pub const fn max(self, other: f32) -> f32 {
|
||||
#[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")]
|
||||
#[inline]
|
||||
pub const fn min(self, other: f32) -> f32 {
|
||||
intrinsics::minnumf32(self, other)
|
||||
intrinsics::minimum_number_nsz_f32(self, other)
|
||||
}
|
||||
|
||||
/// Returns the maximum of the two numbers, propagating NaN.
|
||||
|
||||
@@ -1008,7 +1008,7 @@ pub const fn to_radians(self) -> f64 {
|
||||
#[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")]
|
||||
#[inline]
|
||||
pub const fn max(self, other: f64) -> f64 {
|
||||
intrinsics::maxnumf64(self, other)
|
||||
intrinsics::maximum_number_nsz_f64(self, other)
|
||||
}
|
||||
|
||||
/// Returns the minimum of the two numbers, ignoring NaN.
|
||||
@@ -1035,7 +1035,7 @@ pub const fn max(self, other: f64) -> f64 {
|
||||
#[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")]
|
||||
#[inline]
|
||||
pub const fn min(self, other: f64) -> f64 {
|
||||
intrinsics::minnumf64(self, other)
|
||||
intrinsics::minimum_number_nsz_f64(self, other)
|
||||
}
|
||||
|
||||
/// Returns the maximum of the two numbers, propagating NaN.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::hint::black_box;
|
||||
use std::num::FpCategory as Fp;
|
||||
use std::ops::{Add, Div, Mul, Rem, Sub};
|
||||
|
||||
@@ -32,7 +33,7 @@ trait TestableFloat: Sized {
|
||||
const LNGAMMA_APPROX_LOOSE: Self = Self::APPROX;
|
||||
const ZERO: Self;
|
||||
const ONE: Self;
|
||||
|
||||
const SNAN: Self;
|
||||
const MIN_POSITIVE_NORMAL: Self;
|
||||
const MAX_SUBNORMAL: Self;
|
||||
/// Smallest number
|
||||
@@ -82,6 +83,9 @@ impl TestableFloat for f16 {
|
||||
const LNGAMMA_APPROX_LOOSE: Self = 1e-1;
|
||||
const ZERO: Self = 0.0;
|
||||
const ONE: Self = 1.0;
|
||||
// We rely on NAN having an all-0 payload, so the signaling bit is the least significant
|
||||
// non-0 bit, and that gets toggled by the "-1".
|
||||
const SNAN: Self = Self::from_bits(Self::NAN.to_bits() - 1);
|
||||
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
|
||||
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
|
||||
const TINY: Self = Self::from_bits(0x1);
|
||||
@@ -125,6 +129,9 @@ impl TestableFloat for f32 {
|
||||
const LNGAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 };
|
||||
const ZERO: Self = 0.0;
|
||||
const ONE: Self = 1.0;
|
||||
// We rely on NAN having an all-0 payload, so the signaling bit is the least significant
|
||||
// non-0 bit, and that gets toggled by the "-1".
|
||||
const SNAN: Self = Self::from_bits(Self::NAN.to_bits() - 1);
|
||||
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
|
||||
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
|
||||
const TINY: Self = Self::from_bits(0x1);
|
||||
@@ -153,6 +160,9 @@ impl TestableFloat for f64 {
|
||||
const LNGAMMA_APPROX_LOOSE: Self = 1e-4;
|
||||
const ZERO: Self = 0.0;
|
||||
const ONE: Self = 1.0;
|
||||
// We rely on NAN having an all-0 payload, so the signaling bit is the least significant
|
||||
// non-0 bit, and that gets toggled by the "-1".
|
||||
const SNAN: Self = Self::from_bits(Self::NAN.to_bits() - 1);
|
||||
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
|
||||
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
|
||||
const TINY: Self = Self::from_bits(0x1);
|
||||
@@ -191,6 +201,9 @@ impl TestableFloat for f128 {
|
||||
const LNGAMMA_APPROX_LOOSE: Self = 1e-10;
|
||||
const ZERO: Self = 0.0;
|
||||
const ONE: Self = 1.0;
|
||||
// We rely on NAN having an all-0 payload, so the signaling bit is the least significant
|
||||
// non-0 bit, and that gets toggled by the "-1".
|
||||
const SNAN: Self = Self::from_bits(Self::NAN.to_bits() - 1);
|
||||
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
|
||||
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
|
||||
const TINY: Self = Self::from_bits(0x1);
|
||||
@@ -668,11 +681,22 @@ const fn flt (x: Float) -> Float { x }
|
||||
assert_biteq!(flt(9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
|
||||
assert_biteq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY);
|
||||
assert_biteq!(flt(-9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY);
|
||||
assert_biteq!(Float::NAN.min(9.0), 9.0);
|
||||
assert_biteq!(Float::NAN.min(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).min(Float::NAN), 9.0);
|
||||
assert_biteq!(flt(-9.0).min(Float::NAN), -9.0);
|
||||
// We add black_box for the NAN tests as that used to be able to trigger miscompilations.
|
||||
assert_biteq!(Float::NAN.min(black_box(9.0)), 9.0);
|
||||
assert_biteq!(black_box(Float::NAN).min(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).min(black_box(Float::NAN)), 9.0);
|
||||
assert_biteq!(black_box(flt(-9.0)).min(Float::NAN), -9.0);
|
||||
assert!(Float::NAN.min(Float::NAN).is_nan());
|
||||
// FIXME(llvm21): LLVM miscompiles the fallback impl on aarch64 and likely other targets
|
||||
// (https://github.com/llvm/llvm-project/issues/176624). When we require LLVM 22,
|
||||
// remove the ui test `tests/ui/float/minmax.rs` and unconditionally enable the test here.
|
||||
if cfg!(miri) {
|
||||
assert_biteq!(Float::SNAN.min(black_box(9.0)), 9.0);
|
||||
assert_biteq!(black_box(Float::SNAN).min(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).min(black_box(Float::SNAN)), 9.0);
|
||||
assert_biteq!(black_box(flt(-9.0)).min(Float::SNAN), -9.0);
|
||||
}
|
||||
assert!(Float::SNAN.min(Float::SNAN).is_nan());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,11 +723,22 @@ const fn flt (x: Float) -> Float { x }
|
||||
assert_biteq!(flt(9.0).max(Float::NEG_INFINITY), 9.0);
|
||||
assert_biteq!(Float::NEG_INFINITY.max(-9.0), -9.0);
|
||||
assert_biteq!(flt(-9.0).max(Float::NEG_INFINITY), -9.0);
|
||||
assert_biteq!(Float::NAN.max(9.0), 9.0);
|
||||
assert_biteq!(Float::NAN.max(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).max(Float::NAN), 9.0);
|
||||
assert_biteq!(flt(-9.0).max(Float::NAN), -9.0);
|
||||
// We add black_box for the NAN tests as that used to be able to trigger miscompilations.
|
||||
assert_biteq!(Float::NAN.max(black_box(9.0)), 9.0);
|
||||
assert_biteq!(black_box(Float::NAN).max(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).max(black_box(Float::NAN)), 9.0);
|
||||
assert_biteq!(black_box(flt(-9.0)).max(Float::NAN), -9.0);
|
||||
assert!(Float::NAN.max(Float::NAN).is_nan());
|
||||
// FIXME(llvm21): LLVM miscompiles the fallback impl on aarch64 and likely other targets
|
||||
// (https://github.com/llvm/llvm-project/issues/176624). When we require LLVM 22,
|
||||
// remove the ui test `tests/ui/float/minmax.rs` and unconditionally enable the test here.
|
||||
if cfg!(miri) {
|
||||
assert_biteq!(Float::SNAN.max(black_box(9.0)), 9.0);
|
||||
assert_biteq!(black_box(Float::SNAN).max(-9.0), -9.0);
|
||||
assert_biteq!(flt(9.0).max(black_box(Float::SNAN)), 9.0);
|
||||
assert_biteq!(black_box(flt(-9.0)).max(Float::SNAN), -9.0);
|
||||
}
|
||||
assert!(Float::SNAN.max(Float::SNAN).is_nan());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
#![allow(internal_features)]
|
||||
#![allow(unnecessary_transmutes)]
|
||||
|
||||
#[path = "../utils/mod.rs"]
|
||||
mod utils;
|
||||
use std::any::type_name;
|
||||
use std::cmp::min;
|
||||
use std::fmt::{Debug, Display, LowerHex};
|
||||
use std::hint::black_box;
|
||||
use std::{f32, f64};
|
||||
|
||||
#[path = "../utils/mod.rs"]
|
||||
mod utils;
|
||||
use utils::check_nondet;
|
||||
|
||||
/// Compare the two floats, allowing for $ulp many ULPs of error.
|
||||
@@ -70,7 +70,6 @@ fn main() {
|
||||
test_fast();
|
||||
test_algebraic();
|
||||
test_fmuladd();
|
||||
test_min_max_nondet();
|
||||
test_non_determinism();
|
||||
}
|
||||
|
||||
@@ -1425,19 +1424,13 @@ fn test_operations_f64(a: f64, b: f64, c: f64) {
|
||||
test_operations_f64(1.1, 1.2, 1.3);
|
||||
}
|
||||
|
||||
/// `min` and `max` on equal arguments are non-deterministic.
|
||||
fn test_min_max_nondet() {
|
||||
check_nondet(|| f16::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f16::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f32::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f32::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f64::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f64::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f128::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f128::max(0.0, -0.0).is_sign_positive());
|
||||
}
|
||||
|
||||
fn test_non_determinism() {
|
||||
if cfg!(force_intrinsic_fallback) {
|
||||
// Skip this test when we use the fallback bodies, as that one is deterministic.
|
||||
// (CI sets `--cfg force_intrinsic_fallback` together with `-Zmiri-force-intrinsic-fallback`.)
|
||||
return;
|
||||
}
|
||||
|
||||
use std::intrinsics::{
|
||||
fadd_algebraic, fadd_fast, fdiv_algebraic, fdiv_fast, fmul_algebraic, fmul_fast,
|
||||
frem_algebraic, frem_fast, fsub_algebraic, fsub_fast,
|
||||
@@ -1541,6 +1534,16 @@ fn test_operations_f128(a: f128, b: f128) {
|
||||
test_operations_f64(19., 11.);
|
||||
test_operations_f128(25., 18.);
|
||||
|
||||
// min/max signed zero nondet
|
||||
check_nondet(|| f16::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f16::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f32::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f32::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f64::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f64::max(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f128::min(0.0, -0.0).is_sign_positive());
|
||||
check_nondet(|| f128::max(0.0, -0.0).is_sign_positive());
|
||||
|
||||
// SNaN^0 = (1 | NaN)
|
||||
check_nondet(|| f32::powf(F32_SNAN, 0.0).is_nan());
|
||||
check_nondet(|| f64::powf(F64_SNAN, 0.0).is_nan());
|
||||
|
||||
@@ -541,6 +541,12 @@ fn test_simd() {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if cfg!(force_intrinsic_fallback) {
|
||||
// Skip this test when we use the fallback bodies, as that one is deterministic.
|
||||
// (CI sets `--cfg force_intrinsic_fallback` together with `-Zmiri-force-intrinsic-fallback`.)
|
||||
return;
|
||||
}
|
||||
|
||||
// Check our constants against std, just to be sure.
|
||||
// We add 1 since our numbers are the number of bits stored
|
||||
// to represent the value, and std has the precision of the value,
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
use std::intrinsics;
|
||||
use std::mem::{discriminant, size_of, size_of_val, size_of_val_raw};
|
||||
|
||||
#[path = "../../utils/mod.rs"]
|
||||
mod utils;
|
||||
use utils::check_nondet;
|
||||
|
||||
struct Bomb;
|
||||
|
||||
impl Drop for Bomb {
|
||||
@@ -36,20 +40,7 @@ fn main() {
|
||||
// Skip this test when we use the fallback bodies, as that one is deterministic.
|
||||
// (CI sets `--cfg force_intrinsic_fallback` together with `-Zmiri-force-intrinsic-fallback`.)
|
||||
if !cfg!(force_intrinsic_fallback) {
|
||||
let mut saw_true = false;
|
||||
let mut saw_false = false;
|
||||
|
||||
for _ in 0..50 {
|
||||
if intrinsics::is_val_statically_known(0) {
|
||||
saw_true = true;
|
||||
} else {
|
||||
saw_false = true;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
saw_true && saw_false,
|
||||
"`is_val_statically_known` failed to return both true and false. Congrats, you won the lottery!"
|
||||
);
|
||||
check_nondet(|| intrinsics::is_val_statically_known(0));
|
||||
}
|
||||
|
||||
intrinsics::forget(Bomb);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//FIXME(llvm21) This should be a library test, but old LLVM miscompiles things so we can't just
|
||||
// test this properly everywhere. Once we require LLVM 22, remove this test and enable the
|
||||
// commented-out tests in `library/coretests/tests/floats/mod.rs` instead.
|
||||
//@ min-llvm-version: 22
|
||||
//@ run-pass
|
||||
|
||||
use std::hint::black_box;
|
||||
|
||||
const SNAN32: f32 = f32::from_bits(f32::NAN.to_bits() - 1);
|
||||
const SNAN64: f64 = f64::from_bits(f64::NAN.to_bits() - 1);
|
||||
|
||||
fn main() {
|
||||
assert_eq!(SNAN32.min(black_box(9.0)), 9.0f32);
|
||||
assert_eq!(black_box(SNAN32).min(-9.0), -9.0f32);
|
||||
assert_eq!((9.0f32).min(black_box(SNAN32)), 9.0f32);
|
||||
assert_eq!(black_box(-9.0f32).min(SNAN32), -9.0f32);
|
||||
|
||||
assert_eq!(SNAN64.min(black_box(9.0)), 9.0f64);
|
||||
assert_eq!(black_box(SNAN64).min(-9.0), -9.0f64);
|
||||
assert_eq!((9.0f64).min(black_box(SNAN64)), 9.0f64);
|
||||
assert_eq!(black_box(-9.0f64).min(SNAN64), -9.0f64);
|
||||
}
|
||||
@@ -21,6 +21,8 @@ const fn minmax() {
|
||||
let nan = f32::NAN;
|
||||
// MIPS hardware except MIPS R6 treats f32::NAN as SNAN. Clear the signaling bit.
|
||||
// See https://github.com/rust-lang/rust/issues/52746.
|
||||
// The "-1" works because we rely on `NAN` to have an all-0 payload, so the signaling
|
||||
// bit is the least significant non-zero bit.
|
||||
#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
|
||||
let nan = f32::from_bits(f32::NAN.to_bits() - 1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user